Stack Overflow
{{#include ../../banners/hacktricks-training.md}}
What is a Stack Overflow
スタックオーバーフローは、プログラムがスタックに割り当てられたよりも多くのデータを書き込むときに発生する脆弱性です。この余分なデータは隣接するメモリ空間を上書きし、有効なデータの破損、制御フローの混乱、そして潜在的には悪意のあるコードの実行を引き起こします。この問題は、入力に対して境界チェックを行わない安全でない関数の使用によってしばしば発生します。
この上書きの主な問題は、保存された命令ポインタ (EIP/RIP) と保存されたベースポインタ (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);
}
スタックオーバーフローのオフセットを見つける
スタックオーバーフローを見つける最も一般的な方法は、非常に大きな入力の As を与えることです(例: python3 -c 'print("A"*1000)')そして、アドレス 0x41414141 にアクセスしようとしたことを示す Segmentation Fault を期待します。
さらに、スタックオーバーフローの脆弱性があることがわかったら、リターンアドレスを上書きするために必要なオフセットを見つける必要があります。そのためには、通常 De Bruijn シーケンスが使用されます。これは、サイズ k のアルファベットと長さ n の部分列に対して、長さ n のすべての可能な部分列がちょうど一度だけ連続した部分列として現れる 循環シーケンスです。
この方法により、手動で EIP を制御するために必要なオフセットを特定する代わりに、これらのシーケンスの1つをパディングとして使用し、上書きされたバイトのオフセットを見つけることが可能です。
これには 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
スタックオーバーフローの悪用
オーバーフロー中(オーバーフローサイズが十分大きいと仮定すると)、スタック内のローカル変数の値を上書きすることができ、保存されたEBP/RBPおよびEIP/RIP(またはそれ以上)に到達します。
この種の脆弱性を悪用する最も一般的な方法は、戻りアドレスを変更することで、関数が終了すると制御フローがユーザーが指定したポインタの場所にリダイレクトされることです。
しかし、他のシナリオでは、スタック内のいくつかの変数の値を上書きするだけで悪用が可能な場合もあります(簡単なCTFチャレンジのように)。
Ret2win
この種のCTFチャレンジでは、バイナリ内に 決して呼び出されない 関数があり、勝つために呼び出す必要があります。これらのチャレンジでは、戻りアドレスを上書きするオフセットを見つけ、呼び出す関数のアドレスを見つけるだけで済みます(通常、ASLRは無効になります)ので、脆弱な関数が戻ると、隠れた関数が呼び出されます:
{{#ref}} ret2win/ {{#endref}}
スタックシェルコード
このシナリオでは、攻撃者はスタックにシェルコードを配置し、制御されたEIP/RIPを悪用してシェルコードにジャンプし、任意のコードを実行することができます:
{{#ref}} stack-shellcode/ {{#endref}}
ROP & Ret2...技術
この技術は、前の技術に対する主要な保護を回避するための基本的なフレームワークです:実行可能なスタックなし(NX)。これにより、バイナリ内の既存の命令を悪用して任意のコマンドを実行する他のいくつかの技術(ret2lib、ret2syscall...)を実行することが可能になります:
{{#ref}} ../rop-return-oriented-programing/ {{#endref}}
ヒープオーバーフロー
オーバーフローは常にスタック内で発生するわけではなく、例えばヒープ内で発生することもあります:
{{#ref}} ../libc-heap/heap-overflow.md {{#endref}}
保護の種類
脆弱性の悪用を防ぐためのいくつかの保護があります。詳細は以下を確認してください:
{{#ref}} ../common-binary-protections-and-bypasses/ {{#endref}}
{{#include ../../banners/hacktricks-training.md}}