天枢信息安全社团2015年招新


CTF-PWN入门指导与学习路线

PWN是一个黑客俚语,由"own"引申出来,含意是在对战中处在胜利的优势,或是竞争对手处在完全惨败的情形。在信息安全领域中,包括服务器或PC或是应用程序,PWN在这一方面的意思是攻破("to compromise")或是控制,取得未授权的访问进而入侵系统。

PWN类题目属于二进制安全的重要分支,是对目标程序在逆向工程基础上进行漏洞挖掘和利用,入门难度相对比较大。大多数PWN题目关注x86和x64体系结构,所以入门时应该首先学习8086汇编语言。

PWN类题目一般的形式是这样的:
只提供和Linux或Windows下某程序的可执行文件给参赛选手,选手在本地逆向分析找到漏洞利用方法;服务器中同时在某一端口运行程序,在程序目录下放置flag.txt,里面写上flag信息;选手连接远程服务器,利用漏洞使运行的程序更改运行流程,拿到flag。

推荐入门工具:OllyDBG、IDA。需要的资料已经以pdf形式提供(仍然建议大家使用正版书),下面以需要了解的知识点描述学习轨迹:
入门---->
基本的汇编语句与寄存器(PUSH、MOV、JMP、CALL等指令、EAX、ECX、EIP、ESP、EBP、ESI、EDI等寄存器、8086与80386)-->
栈(局部变量在栈上的分布、栈保存的其他重要数据、小端序)-->
函数调用发生了什么(返回地址的保存、栈帧、函数参数传递)-->
数据与指令有什么不同(存储位置?数据能执行吗?)-->
一些简单函数执行流程(C语言与汇编语言、观察printf(“Hello world”))-->
漏洞利用方法(覆盖邻接变量、修改返回地址)-->
一些常见的漏洞类型(缓冲区溢出(scanf,gets)、格式化字符串(printf)、堆溢出、整数溢出)->
进阶---->
Shellcode书写-->
突破保护(GS、DEP、ASLR、JMP ESP,ret2libc,ROP)-->
…----->
招新的题目难度会很低,大家首先应学习、掌握入门部分的内容。

考虑到大家的基础,下面以Windows下的一个有缓冲区溢出漏洞的简单程序example.exe为大家示例。
漏洞在函数stack_over_flow()的gets()函数中,缓冲区只有16个字节,如果输入长度大于16,将覆盖很多重要数据包括stack_over_flow()函数的返回地址(需要学习栈相关内容)。远程服务器运行的例程的目录下有一个flag.txt文件,存放flag,而程序中已经写好了读取的代码readfile()但并没有被调用。我们通过缓冲区溢出覆盖到stack_over_flow()函数的返回地址,让其返回到readfile()就可以拿到flag。


为了便于大家学习,首先给出程序的源码。但要知道,比赛题目并不给出源码。
#include<stdio.h>
#include<stdlib.h>
int stack_over_flow()
{
char Password[16] = { 0, };
gets(Password);
return 0;
}
void readfile()
{
FILE* fp;
char FileStr[20] = { 0, };
printf("You‘ve got the flag:\n");
if (fp = fopen("flag.txt", "r"))
{
fgets(FileStr, 48, fp);
printf("%s\n", FileStr);
fclose(fp);
}
else
printf("File error!\n");
exit(0);
}
int main()
{
printf("Welcome!\n");
printf("Please input your password(within 8 characters):\n");
stack_over_flow();
printf("Good Bye!\n");
return 0;
}

可以使用IDA静态分析该程序,然后使用OllyDBG调试这一程序。IDA和OllyDBG工具的基本使用方法不再说明。运行程序,发现输出了两行提示然后要求用户输入。

逆向分析,找到main函数的位置为00401160,观察还可以发现00401520为printf(),00401005就是stack_over_flow()函数。在反汇编窗口继续寻找可以找到readfile()函数的入口地址00401090。

从00401005跳到00401030,stack_over_flow()就在这个位置。004011D0一定就是scanf()了。

如下图所示,测试输入了8个‘A’(16进制表示为0x41),发现在栈如下所示。总共16个字节的空间,后面四个字节为之前保存的ebp,再下面四个字节处00401197就是本函数的返回地址。考虑输入20个‘A’,然后把返回地址覆盖为readfile()函数的入口地址00401090。这样当程序返回的时候就从栈中取出00401090作为EIP寄存器的内容,于是然后程序执行跳到了EIP处,读取文件显示flag。

由于要输入的一些字符并不能从键盘中打印出来,我们可以借助Python输出重定向。

输入的示例:python -c "print ‘A‘*20+‘\x90‘+‘\x10‘+‘\x40‘+‘\x00‘" | example.exe

? 管道符号:‘|’,用来把Python输出重定向到example.exe的输入。
? ‘A’:20个‘A’用来填充栈内空间。
? 00401090:通过逆向工程的手段找到的readfile()函数的入口地址。因为是16进制所以有’\x’;之所以采取这样的顺序是因为intel CPU的小端序(需要自行学习)。填的位置正好位于stack_over_flow()函数的返回地址处,相当于修改了这一地址。


Written by default from TS2013.
有问题可以联系我和其他学长帮助解决。我的邮箱:
学习安全知识技能需要热情,大家加油!