PWN入门习题
果然人闲下来就会给自己找事啊。。。。。
首先,pwn是什么
PWN = 利用程序的漏洞,让程序执行它本不应该执行的操作。
最常见的目标是:获取 Shell(也就是获得命令行控制权,可以在目标机器上执行任意命令)。
似乎就是找漏洞,然后尝试利用漏洞执行提权/执行想要进行的操作
比如栈溢出流程就是发现—破解—利用,用无效数据占位后,让有效数据进入重要执行位置,达成利用
pwn需要的一些基础知识
以下摘自ai
汇编
什么是汇编语言:
首先,计算机的机器语言是二进制的机器码,一般用十六进制呈现,我们理解起来相当困难,于是就有了汇编语言——一种简短的,带有含义的英文字符(比如MOV AL, 61H),官方一点叫做助记符,同时保留了对硬件最基础的操作能力
汇编语言的特点
1,汇编语言和机器的cpu有强绑定性
不同 CPU(如 x86、ARM、RISC-V)有完全不同的汇编指令集,x86 的汇编代码无法直接在 ARM 芯片上运行(比如电脑和手机的 CPU 汇编指令不通用)
2,极致的性能和可控性
因为直接操作寄存器、内存、硬件端口,汇编代码执行效率几乎是最高的,能精准控制硬件的每一个动作;
3,可读性差、开发效率低
没有高级语言的变量、函数、循环结构(需手动实现),代码冗长且难维护,通常只在关键场景使用。
简单汇编代码示例:
下面是一段最基础的汇编代码,功能是计算 1+2 并将结果存入指定寄存器:
; 汇编代码注释(以分号开头)
section .text ; 代码段(存放执行指令)
global _start ; 程序入口点
_start:
mov eax, 1 ; 将数值1移入eax寄存器(mov=move,数据传输指令)
add eax, 2 ; 将eax寄存器的值加2(add=addition,加法指令)
; 此时eax寄存器的值为3,就是1+2的结果
; 程序退出(Linux系统调用)
mov eax, 60 ; 系统调用号60=exit
xor edi, edi ; 退出码0
syscall ; 执行系统调用
这里只是单纯的计算了一加二,没有输出结果;想要看到输出结果还需要添加write相关的语句
(估计还得单开一章专门理解这玩意,先写到这)
寄存器
这里直接拉大佬的文章
BUUCTF
[test_your_nc]
nc连接后输入命令,查找flag

flag{5df8a84f-a3fc-426e-8d79-4b9d816b0570}
rip
打开链接先看看,打不开上nc

结果不太好
那就去看看题目附件,先查一下是多少位的

DIE看一眼,64位,拉去ida
在system函数中发现点东西

/bin/sh是我们要找的东西
这玩意是系统的默认命令行解释器(shell),接下来摘自ai
什么是/bin/sh
/bin/sh核心概念
- 定义:/bin/sh 是 Bourne shell 的可执行文件路径,是 Unix 系统中最基础的命令行解释器
- 作用:它允许用户与操作系统交互,执行命令、运行脚本程序
- 位置:位于根目录下的 bin 目录中,bin 是 binary(二进制文件)的缩写
/bin/sh主要功能
- 命令解释:解析并执行用户输入的命令
- 脚本执行:运行 shell 脚本程序
- 环境管理:管理系统环境变量
- 程序启动:作为其他程序的启动器
/bin/sh与其他 shell 的关系
在不同的 Linux 发行版中,/bin/sh 可能是:
- 指向 bash (Bourne Again Shell) 的符号链接(如在大多数系统中)
- 指向 dash (Debian Almquist Shell) 的符号链接(如在 Ubuntu 等系统中)
-
或者是其他兼容 POSIX 标准的 shell
为什么要找/bin/sh
1. 获取系统控制权
/bin/sh是 Unix/Linux 系统的命令行解释器,通过获取一个 shell,攻击者可以:
- 直接与系统交互
- 执行任意命令
- 访问和修改文件
- 进一步提升权限
- 维持对系统的访问
2. 漏洞利用的最终目标
大多数漏洞利用的最终目标是获取一个交互式 shell,因为这提供了最大的灵活性和控制权:
- 缓冲区溢出漏洞
- 格式化字符串漏洞
- 堆漏洞
- 等各种内存安全漏洞
3. 在漏洞利用中的具体用途
(1) 作为 execve 的参数
c
运行
execve("/bin/sh", NULL, NULL);
这是获取 shell 的经典方法,直接执行 shell 程序
(2) 在 ROP 链中
攻击者会在内存中寻找/bin/sh字符串的地址,作为 ROP 链的参数使用
(3) 环境变量中的位置
有时攻击者会将/bin/sh放在环境变量中,然后找到它的地址来使用
4. 为什么选择 /bin/sh 而不是其他 shell
/bin/sh几乎存在于所有 Unix/Linux 系统中- 它是系统最基础的 shell,通常不会被删除
- 它的路径固定,容易在漏洞利用中定位
- 它足够简单,在受限环境中也能运行
5. 现代防御机制
现代系统有各种防御机制来防止这种攻击:
- ASLR(地址空间布局随机化)
- 栈保护
- 不可执行内存
- 沙箱技术
什么是 Shell
Shell(壳)是计算机操作系统中用户与内核之间的接口程序,它允许用户通过命令行与计算机系统进行交互。
Shell 的核心概念
- 定义:Shell 是一个命令行解释器,它接收用户输入的命令,将其转换为操作系统能够理解的指令,并执行这些指令
- 作用:作为用户和操作系统内核之间的 "翻译者" 和 "桥梁"
- 名称由来:它像一个 "壳" 一样包裹在操作系统内核的外面,保护内核并提供交互界面
Shell 的主要功能
- 命令解释与执行:解析用户输入的命令并执行
- 输入输出重定向:控制命令的输入来源和输出目的地
- 管道操作:将一个命令的输出作为另一个命令的输入
- 环境变量管理:设置和管理系统环境变量
- 脚本编程:支持编写自动化脚本
- 作业控制:管理前台和后台运行的程序
常见的 Shell 类型
- Bourne Shell (/bin/sh):最早的 Unix Shell,简洁高效
- Bash (/bin/bash):Bourne Again Shell,最流行的 Linux Shell
- C Shell (/bin/csh):语法类似 C 语言的 Shell
- Z Shell (/bin/zsh):功能丰富的现代 Shell
- Fish Shell:用户友好的交互式 Shell
Shell 的工作原理
- 用户在终端输入命令
- Shell 读取并解析命令
- Shell 将命令转换为系统调用
- 操作系统内核执行相应操作
- Shell 将结果返回给用户
阿巴阿巴阿巴阿巴阿巴
回到正题
接着开始敲脚本,首先确定这个函数的位置

这里我们能看到lea函数,简单理解lea,就是将目标位置的地址存储到寄存器中,像这里就存给了di,同时我们也能锁定到位置000000000040118A,接着先看下面,call sysytem,回归给系统操作,这里会接受一个字符串作为执行内容,本条指令在伪c中的状态是这样的:

那么这个/bin/sh哪里来的?
咱们跟着回去看command
![]()
command标号中,使用db定义了/bin/sh的字节段,0表示定义结束,这里的字符串和上面的system结合起来的效果就是伪c中的效果
那么又有一个问题,system需要接受字符串内容,那么它是从哪里读取到的字符串?
- 32位:参数在栈上。
- 64位 Windows:参数在 RCX 寄存器。
- 64位 Linux:参数在 RDI 寄存器。
这里我们回到题目,就能发现它是乌班图系统,算是linux,所以这里是rdi
好了,现在确定了目标位置,这里我们还缺覆盖大小,回到主函数

这里最简单的方式是看最开始的声明,s段有f,也就是十五个字节的缓冲区需要覆盖,然而这里使用的输入函数是gets,没有任何安全机制,能造成缓冲区溢出,所以这里缓冲区,也就是空区,需要十五个字符,但我们还要再加上八个字节,因为这里将rbp存入了栈中,64位中,rbp是八字节存储,32位则是四位【这里补充一点,push和pop在8086中,最小操作单位是2字节,32位是4字节,64位是8字节】接着,我们就覆盖到了我们心心念念的返回地址
为什么返回地址会在这里?当我们call一个函数的时候,call指令会入下一条命令的地址,这里我们敢说它不是第一个执行的代码段的原因就是,在任何程序执行之前,cpu都在执行dos,执行main相当于call main,必然会留下执行完mian后要去的位置【当然,这里的dos是针对8086cpu说的,这玩意能简单的理解为系统】
而跳转回执行位置的函数就是retn,8086叫ret。将先前存入的地址交给cs:ip,继续执行未执行完的指令【leave的作用是销毁栈帧,让sp=bp,拆分后有一步是pop rbp,这里弹出的就是咱们先前push的rbp,很不幸的是,咱们已经覆盖掉了,但这玩意和返回地址没什么关系,所以放心覆盖】
这里分配rsp分配出的大小和s不同的原因是为了内存对齐,cpu查看,执行数据和命令是按照块来读的,比如4,8,16字节,这里分配了十五字节,为了内存对其就要16字节。而为了防止某些特殊指令报错,将分配空间设置为16的倍数是很合理的选择
那么为什么gets还是要覆盖十五个字节?因为调用get的时候,get会从上一个被存入的数值寄存器读取值作为写入位置,这里结合体感,也就是rid了,rdi的位置是rbp+s,这里s是-0xfh,这样就保证了覆写的准确性
要注意栈中压入数据和sub rsp的先后关系,确保栈图正确
接下来就是愉快的敲代码环节了
from pwn import * #引入pwntools库
p=remote('node5.buuoj.cn',27518)
#64 +8 32 +4
#这是我们找的system地址
payload = b'a'*(0xF+8)+p64(0x040118A) #构造攻击负载
p.sendline(payload) #发送载荷
p.interactive()
传入代码后,catflag就行了【这里没有演示,本来是buu的题,但是buu似乎换地方了,新网站还没玩明白,或许是服务器在维护,nc过去没反应,相当难用】
怕是只能写到这里了,什么时候玩明白新buu再继续写
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)