赛博协会新生测验 Web Writeup

赛博协会新生测验 Web Writeup

安全 2719 字 / 6 分钟
AI 总结 以下AI总结内容由腾讯混元大模型生成

本文为一位名为E99p1ant的技术博客作者分享的关于网络安全竞赛(CTF)的Web题目的解题过程及总结。作者通过分析和调试,成功破解了两道Web题目,分别是基于PHP的代码审计题目和Web应用程序逆向题目。

题目一:Web1 - 赛博协会新生测验

这道题目主要考察了对PHP基础知识的掌握程度以及对Web应用程序的安全性认识。作者在解题过程中遇到了一些困难,但最终通过逆向工程和代码审计找到了答案。

  1. 假的不杭电认证:作者在登录认证界面上花费了大量时间,最终通过F12查看网络请求发现了一个302跳转,从而找到了隐藏的登录页面。

  2. 首富俱乐部:作者通过尝试不同的输入方式,最终通过科学计数法找到了一个包含flag文件的页面,并成功获取了flag。

  3. 三道代码审计:作者依次攻破了三道代码审计题目,每道题目都需要对GET请求中的参数进行仔细检查和安全处理。

题目二:Web2 - 基础设施黑客马拉松

这道题目主要考察了Web应用程序的安全性和编程技巧。作者通过分析网页源代码和利用相关工具,成功找到了四段flag并最终获得了完整的flag。

  1. 简单的Web应用程序漏洞:作者首先访问了一个网站,发现了一个302跳转,通过抓包和分析,发现了网站使用了Flash进行功能实现。

  2. 科学计数法与弱类型比较:在解决一道代码审计题目时,作者通过传递科学计数法的数字和使用==运算符,成功找到了flag。

  3. 正则表达式与GET参数:在解密另一道题目时,作者利用了正则表达式的特性,通过对GET参数进行处理,成功找到了flag。

  4. 字符串比较与数组传递:最后一道题目需要比较两个字符串变量,作者通过传递数组的方式成功找到了flag。

总的来说,本文通过详细的解题过程和总结,展示了在网络安全竞赛中如何应对各种挑战,提高自己的技能水平。

昨晚补完刀剑后又看了一会手机,睡得有点晚。今天早上十点多才起,想起赛博今天有新生测验,马上跑下床打开电脑。 嘛,还是挺有意思的,学到了不少东西。虽然刚开始跑偏了,差点想放弃2333 那么,我们来看看这些题吧:

Web1

这是一个搞逆向的学长出的 web 题。嘛,确实有些地方有点不太“安全“吧,之后我会提到。

假的杭电认证

进入题目,输入1进入第一题,然后直接跳到了连接校内 Wifi 的登录认证界面。这个是最骚的。我整整在这里耗了两个小时……各种尝试都用尽了,还是什么都没有出来。正当想放弃去写代码的时候,开 F12 随便看了一眼——发现他这边的 302 跳转用了两次。从confirm.php跳到了easiest.php再到check_login.html的。 嗯,感觉这个easiest.php有那么点小问题。开 Burp Suite 抓一波包,果然:

flag{Th1s_is_7eh_easiest_Eo2}

嘛,想太复杂了,被自己蠢哭。哈哈哈哈 这边有个小插曲,学长之前把 flag 中的 he 写成了 eh,也就是我拿到的这个,导致进不去下一题,改回去就好了。我还以为自己拿到了假的 flag。

首先你得有一个亿

原以为这个是那种很弱智的把文本框的maxlength改一下就好的,结果发现不行 QAQ 然后连蒙带猜瞎尝试,最后被我试出来: 这个必须要输入科学计数法,比如1e999 然后就可以了!

土豪俱乐部

直接一个do not click me连接,点一下跳到:http://111.231.107.65/exam/flag.php?file=useless.php,页面内容是QXJlIHlvdSBibGluZD8=,根据文件名useless.php以及页面内容base64_decode后得出的信息,我们可以知道这个页面只是为了告诉你这里存在一个文件包含。 我刚开始只是随意填了几个当前目录下的文件,发现仅仅是单纯的包含进来,并没有什么用。 搜了一波,发现居然可以用php://来读取文件源代码!哇!学到了! 直接访问: http://111.231.107.65/exam/flag.php?file=php://filter/read=convert.base64-encode/resource=flag.php

拿到 base64 后的网站源代码,然后解码一下就出来 flag 了:

flag{8ec2re_0F_1NcIu6e}

三道代码审计

后面就是三道代码审计了,套路都是一样的:

第一题

第一题就是在看你学没学过 PHP,哈哈

flag_1="xxx"
$temp = $_GET['number'];
is_numeric($temp)?die("no numeric"):NULL;
if($temp>1336){
die('第一段:'.$flag_1);

传入一个 GET 参数number,然后要比 1336 大,但又不能是数字。我记得很久之前的协会培训,就讲到了 GET 参数传数组进去的骚套路。传数组总会出现一样不到的东西。这里就用上了: http://111.231.107.65/exam/innocent.php?number[]=2333 得到第一段 flag:

1nN0c3nt_

第二题

$flag_2 = 'xxx';
if (isset($_GET['username']) and isset($_GET['password'])) {
if ($_GET['username'] == $_GET['password'])
print 'Your password can not be your username.';
else if (md5($_GET['username']) === md5($_GET['password']))
die('第二段:'.$flag_2);
else
print 'Invalid password';
}

这里要求 GET 请求传进去的usernamepassword参数不能相等,但是他们的 md5 要相等。就是协会之前培训的原题,传数组!! http://111.231.107.65/exam/innocent.php?id=2&username[]=1&password[]=2 拿到第二段 flag:

phPcOde_

第三题

$flag_3 = 'xxx';
if (isset($_GET['flag_3'])) {
if (strcmp($_GET['flag_3'], $flag_3) == 0)
die('第三段:'.$flag_3);
else
print 'No';
}

这里就是对比传进去的flag_3$flag_3变量是否相等,相等出 flag,但我们并不知道$flag_3的值,怎么办呢?三个字,传数组!! http://111.231.107.65/exam/innocent.php?id=3&flag_3[]=233 拿到第三段 flag:

aUd17

三段拼起来,拿到最终的 flag:

flag{1nN0c3nt_phPcOde_aUd17}

后面这三道题其实都是 PHP 弱类型比较的,都是用了==而不是===所引发的问题。之前司大哥在看我 GitHub 上的项目时也指出了我疯狂用弱类型比较的问题。

然后 web1 就做完了!开心!

Web2

这题嘛,就是单纯的代码审计啦~ 但是满满的干货呢!学到了不少东西! 首先直接访问149.28.199.89:2223就 302 跳转到了 4399。(话说这个靠 Flash 吃饭的网站居然还没倒闭?!) Burp Suite 抓包看一下 302 跳转前原页面的内容:

这边的注释里面告诉我们可以以任何请求方式传一个名字为source的参数 传参后就看到了网站的源码,太长了就不贴了。我一段一段来分析吧:

if(strcmp($_GET['password'], $password) == 0)){
	...

又是strcmp和一个不知道的变量比较,用 web1 的套路,传数组!! password[]=1 拿到第一段 flag:

Cyber{

if($b != $c & md5($b) == md5($c)) { 
	...

这个就是很常见的弱类型比较了,$b$c不能相等,但是他们的 md5 要相等。碰撞是不可能的。这里就是用科学计数法啦~ 只要让md5($b)md5($c)的值都是0e开头即可,这样弱类型比较都会把两个值当成数字,然后0==0true,搞定! 百度一波0e开头的 md5,随便挑两个传进去就行。 equal_md5_1=s878926199a&equal_md5_2=s155964671a 拿到第二段 flag:

Aud1t_

if($data=="web_1s_rea1_funny"&& preg_match("/^lalala$/m", $_GET['fake'])&& $_GET['fake']!=="lalala") { 

这个有点意思。 我们首先看这个$data,上面有一句$data = @file_get_contents($_GET['data'],'r'); 。一直是做的 PHP 开发,所以不知道file_get_contents还可这样用。 上网找了下,又是说我们可以传data=php://input进去,然后$data变量就可以读取自我们 POST 进去的数据了。 再把web_1s_rea1_funnyPOST 过去即可。

后面的正则比较,临时抱佛脚现场学了下正则,发现问题在/m这里,定义中是这么说的:

如果设置了 RegExp 对象的 Multiline 属性,$ 也匹配 “\n” 或 “\r” 之前的位置

也就是说会多匹配一个换行符或回车符。这就好办了,我们只需要在fake后面加个换行符就好了。但是问题来了,怎样在 GET 的参数里,也就是 URL 里加上换行符呢? 直接在 URL 里面换行肯定不对,我们只需要找到了换行符 urlencode 编码后的东西就好了,是%0a。 那么就是fake=lalala%0a 拿到第三段 flag:

1s_re0l_

if(!(substr($_GET['boom'], 32) === md5($_GET['boom']))){ 

又是比较,又有 md5,那么怎么办呢?大声喊出那三个字!传数组!! 轻轻松松 boom[]=1 拿到第四段 flag:

InTerest1ng}

拼在一起就是:

Cyber{Aud1t_1s_re0l_InTerest1ng}

整个 Web2 的 payload:

curl 'http://149.28.199.89:2223/?source=1&password%5B%5D=1&equal_md5_1=s878926199a&equal_md5_2=s155964671a&data=php%3A%2F%2Finput&fake=lalala%0a&boom%5B%5D=1' -H 'Pragma: no-cache' -H 'Origin: chrome-extension://aejoelaoggembcahagimdiliamlcdmfm' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: zh-CN,zh;q=0.9' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36' -H 'Content-Type: text/plain' -H 'Accept: */*' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' --data-binary 'web_1s_rea1_funny' --compressed

我做的时候是用部长之前推荐的 Chrome 插件:Restlet Client,可以直接发送 GET / POST 请求。我之前写微信小程序时后端的各种接口都是拿这个测试的。十分的舒服。

总结一下

以上两道题就是这样。真的是学到了不少东西啊。 那么我就来简单总结一下吧,首先是 Web1,学长出这个题的时候装 XAMPP 环境的时候没搞干净啊。直接访问 IP 进入 XAMPP 默认的欢迎界面,然后右上角直接进入看phpinfo()刺不刺激?看到 MySQL 那边都是一片空,我大概推测这题一路下来应该没什么 SQL 注入。 如果绑了数据库,看到了账号密码,想想看会有多刺激2333。

然后 Web1 之前一直耗在那个登录界面,F12 看到页面中被注释掉的 JS 原本是要转到login.php。试了下,发现它会把userNamepwd强制转换成数字,然后输出它们相加的结果。一个计算器哈哈哈。 我也尝试了传科学计数法的数字,它同样也会计算。所以我在之后的“首先你得有一个亿”才会想到也传科学计数法试试。 嘛,总之第一题当时很懵逼,不知道该怎么办才好,抱怨这题怎么这么奇葩。302 跳转抓包后才发现我真的就是想太多。

Web2 满满的干货,让我涨了不少见识。以后开发中也要多注意安全问题啊!

玩得很开心。学长们都很棒!只是菜鸡的我只会做 web 题。QAQ

谢谢老板 Thanks♪(・ω・)ノ


喜欢这篇文章?为什么不打赏一下呢?