关于做题(pwn)过程中的一些细节知识/经验的积累。
工具
gdb
print &id
——查看变量id在内存中的地址;gdb-peda$ pattern_create 50
——创建长度为50的随机字符串;pattern_offset
——查看指定值在输入字符串中的偏移,定位溢出点,需要与pattern_creat
一起使用,如下图;
如下图,发现在44字节之后,我们可以完全覆盖EIP寄存器。
delete 断点号
——删除程序中的断点;bt
——显示所有的函数调用栈帧的信息,每个帧一行;disas /r xxx
——查看xxx函数中对应的汇编指令及其机器码;gdb调试过程中修改指定内存/变量的值,如下图;
-hex 0xXXXXXXXX
——查看堆栈信息,回车可以查看后面栈的内容;
pwntools
- ROP链生成器;
asm()
——将汇编指令转换成真正的shellcode(机器码);fmtstr_payload(autofmt.offset, {printf_got: system_add})
——用来生成格式化字符串漏洞写内存的payload;
第一个参数为offset偏移(通过爆破试出来,如图);
我们可以看到,%p泄漏出了printf栈里面的东西,并且可以发现AAAA也就是“0x41414141”在第十个位置,也就是说格式化字符串在栈的第十个位置,于是我们就可以构造:【泄漏地址】+%10$s,来把password给泄漏出来;
第二个参数是一个字典,意义是往key的地址,写入value的值,比如往0x804a010地址写入数据0xaaaaaaaa。
利用此函数构造的payload示例如下图;
不借助此函数的对应脚本示例如下图;
其他
- 在linux下用vi编辑ELF文件;
基础
- 关于内联汇编——内联arm汇编如图所示;
- libc.so.6文件的作用——在一些题目中,经常可以看到除了提供ELF文件之外还提供了一个libc.so.6文件,其作用如下图:
- Linux ELF文件:三种文件类型——可重定位的目标文件(Relocatable,或者Object File)、可执行文件(Executable)、共享库(Shared Object,或者Shared Library)。
- stripped和not stripped
- 关于one_gadget:解释如下图;一个潜在的gadget需要满足以下条件:A.能够访问到”/bin/sh”字符串;B.调用了exec*系列的函数。
- 关于stack smash:指的是程序存在栈溢出漏洞,但同时存在对栈溢出的检测保护机制__stack_chk_fail(其源码如下图)
由图可知,我们只需要能够覆盖argv这个参数为我们想要的字符串的地址系统在检测到栈溢出时就会打印出我们想要的内容;
这种利用方式主要是利用了canary的报错信息。
- linux中的mprotect()函数。
- 关于ELF的安全防护机制。
序号 | 名称 | 描述 |
---|---|---|
1 | RELRO | RELRO会有Partial RELRO和FULL RELRO,如果开启FULL RELRO,意味着我们无法修改got表 |
2 | Stack | 如果栈中开启Canary found,那么就不能用直接用溢出的方法覆盖栈中返回地址,而且要通过改写指针与局部变量、leak canary、overwrite canary的方法来绕过 |
3 | NX | 如果这个保护开启就是意味着栈中数据没有执行权限,以前的经常用的call esp或者jmp esp的方法就不能使用,但是可以利用rop这种方法绕过 |
4 | PIE | 如果程序开启这个地址随机化选项就意味着程序每次运行的时候地址都会变化,而如果没有开PIE的话那么No PIE (0x400000),括号内的数据就是程序的基地址 |
5 | FORTIFY | FORTIFY_SOURCE机制对格式化字符串有两个限制(1)包含%n的格式化字符串不能位于程序内存中的可写地址。(2)当使用位置参数时,必须使用范围内的所有参数。所以如果要使用%7$x,你必须同时使用1,2,3,4,5和6 |
- 汇编语言堆栈平衡。
- 关于plt和got表:以
puts
函数为例,通过puts_plt
可以直接调用puts
函数;puts_got
里面存的是程序实际运行过程中puts
函数的动态libc地址。
密码
- Ook语言
- Brainfuck语言
- 莫斯密码
- .?!
- base64编码
经验
关键内容如下——
- 对于登陆验证型题目——
在输入比较好控制改变的情况下,可以考虑想办法让输入满足验证条件(一般很少这样处理);
大部分情况下,都是考虑直接绕过验证,跳到验证正确之后的处理流程进行处理。
- 对于给了源代码的题目,要学会从中找到有问题的代码(包括如格式化字符串漏洞等漏洞、以及代码处理逻辑上的一些问题-如下面2个图):
- 对于没什么思路的题目,通用思路如下图——
- 一般来说,即使ELF可执行文件是开启了ASLR保护机制的,其got表中函数的地址的最后三位是不会变化的,这也给暴力破解提供了可能性。
- 对于没有给libc文件的题目,可以通过泄露得到的libc函数地址的最后三位,判定远程libc版本并获取system函数和binsh字符串的偏移(libc database search、libc-database、LibSearcher)。
- 利用qemu动态调试arm架构的elf文件。
- 调试EXP:下述流程一般是针对堆漏洞的,栈的话下断点看比较方便
在exp中的
p = process()
之后 就用gdb.attach(p,'continue')
;
然后在想断下来的地方加个pause()
;
执行exp,程序会停在pause
的地方;
在弹出的gdb窗口上ctrl+c
停下来,查看内存进行调试即可;
这种方法不用管断点下在哪里,比较方便。
- 对开启了PIE保护机制的程序的动态调试方法——主要是如何将断点下在ida中对应的指令处。
pwngdb可以直接 b reabse,可以bcall;
在gdb attach
调试程序运行的时候ctrl+c
断下来,用bt
找到main函数地址,替换后面三位;
在本地调试开启了PIE的程序,可以直接在本机上使用命令sudo sh-c"echo 0 > /proc/sys/kernel/randomize_va_space"
把aslr关了,由于PIE依赖aslr,此时程序的PIE保护机制也会失效;
在gdb中利用b offset
通过ida中看到的偏移地址的最后三位下断点。
binary patch:对二进制文件内容的改变,比如改变关键跳转语句;简单的patch流程为——
在十六进制编辑器中打开对应的ELF文件;
此时文件的内容是所含汇编程序的机器码;
在gdb中用(disas /r xxx)命令可以看到xxx函数中对应的汇编指令及其机器码;
然后定位到关键待修改语句,在十六进制编辑器中对应位置处修改即可。若可执行文件的主函数十分简单,但是程序里却有十分多的函数,说明可执行文件是静态链接编译的。
- 对于基本ROP的绕过;
直接利用
ROPgadget
等工具生成ropchain,需要足够的溢出空间;
对于动态链接的ELF,利用其so文件中的system
和"bin/sh"
;
对于静态链接的ELF,若程序中没有system
函数,则可利用程序中可能存在的int 0x80
(中断信号)或者execve("/bin/sh\x00",0,0)
函数;
一种将"bin/sh\x00"
写入内存的思路(程序开启了栈上不可执行保护,对于此类函数参数一般考虑将其写入.bss
段):利用特殊的gadget
例如是mov dword ptr [ecx],eax
, 将字符串压入eax中,要写入的地址压入ecx中,然后通过这个gadget
就可以将字符串写入想写入的内存了。
- 构造假栈帧——通过溢出,去执行一次read函数,把我们要接下来执行的rop链写到bss的某个地址里去(可以根据用readelf 命令去查一下bss的哪个地方有执行的权力),接着构造假的ebp,让ebp跳转到bss的某个地址中,从而让计算机把那个地址当成栈帧,达到构造假栈帧的目的。
参考题目:lab6
- 栈迁移——在写入空间不够的时候,通过leave_ret这类收尾的代码来把ebp和esp改到某个地址固定的位置,通过控制ret的地址和ebp指针向我们指定的位置写值,通常是一段不完整的rop代码,通过不断迁移把rop代码一段一段的写完,最后通过leave_ret到rop代码上面4字节(x86)来实现rop的调用。
- 在64位的程序中,申请的chunk大小需要比实际的内容长度多0x10,这16个字节用于存chunk的头信息(pre_size和size(各8个字节))。
- 如图这种exp里面最后一行
cn.sendline(‘$0’)
的作用是什么呢,为啥这里面只是把system
函数的got表地址的替换实现了,并没有给参数“bin/sh”
最后就能getshell呢?
作用相当于提供了
system
执行所需要的“bin/sh”
参数;
但这个$0
与当前上下文环境有关,并不是每次都会是bin/sh
;$0
——Shell本身的文件名;
参考:linux shell中特殊字符的意义$0 $1 $$ $# $@;
示例如下图;
- 如下图所示,在IDA中静态分析可判断出第0x45-0x48个字节可以覆盖到函数的返回地址;
但是实际测试发现往后偏移了8位,分析汇编代码发现是由于进行了内存对齐的操作,esp经过了与操作之后就比之前小了8,就往后移了8位,如下图所示——
- 如下图所示,程序中限定了覆盖的return地址前两位不能为
FF
,但是我们填充的shellcode所在的地址前两位就是FF
,如何处理?
通过包含空函数nop和ret指令的ROP指令如下图,然后再return回来执行下一句return到shellcode。
报错及对应解决
gdb attach 进程号
动态调试连接进程失败,报错:ptrace:不允许的操作。
- 使用ROPgadget时报错——ImportError: ERROR: fail to load the dynamic library。
https://www.jianshu.com/p/b120b07d1710
https://www.cnblogs.com/p4nda/p/7224340.html