目次
偵察
実行スクリプト
パラメータ設定
FILE=
スクリプト作成
tee main.py <<EOF
from pwn import *
context.log_level = 'debug'
# Settings
FILE = sys.argv[1]
# context(arch='powerpc')
# elf
elf = ELF(FILE)
EOF
実行
file $FILE
python main.py $FILE
checksec --file=$FILE --format=json | jq
出力例
$ file $FILE
python main.py $FILE
file_name: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=xxx, for GNU/Linux 3.2.0, not stripped
[*] '/path/to/elf'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No
checksec --file=$FILE --format=json | jq
{
"file_name": {
"relro": "partial",
"canary": "yes",
"nx": "yes",
"pie": "no",
"rpath": "no",
"runpath": "no",
"symbols": "yes",
"fortify_source": "no",
"fortified": "0",
"fortify-able": "0"
}
}
制御機構 | 値 | pwntools | checksec | 意味 |
---|---|---|---|---|
RELRO (RELocation Read Only) | No/Partial/Full | x | x | 書き込み禁止 |
STACK CANARY | Yes/No | x | x | 開始時にスタックにランダム値(カナリヤ)を格納 |
NX (No eXecute) | Yes/No | x | x | スタック領域のコード実行禁止 |
PIE (Position Independent Executable) | Yes/No | x | x | 実行ファイル配置アドレスをランダム化 |
RPATH | Yes/No | - | x | 実行ファイル共有ライブラリのサーチリストを格納 |
RUNPATH | Yes/No | - | x | RPATH + LD_LIBRALY_PATH優先 |
FORTIFY | Yes/No | - | x | GCC、GLIBC のセキュリティ機能 |
SHSTK (Shadow Stack) | x | - | リターンアドレス監視 (Intel) | |
IBT (Indirect BranchTracking) | x | - | 不正間接分岐監視 (Intel) | |
Stripped | Yes/No | x | - |
PIE
絶対アドレスは変わるが、相対アドレスは変わらない。
どこかの関数の絶対アドレスがわかれば、任意の関数の絶対アドレスを算出可能。
詳細調査
アドレス一覧
from pwn import *
elf = ELF('/home/kali/Desktop/download/vuln')
# すべての関数のアドレスを表示
for func in sorted(elf.symbols):
try:
print(f'{func}: {hex(elf.symbols[func])}')
except:
pass
Fuzzing
import click
from pwn import *
def pre(server: remote, timeout: int):
pass
def post(server: remote, timeout: int):
pass
@click.command()
@click.option('-i', '--ip', prompt='IP address', help='The IP address to fuzz.')
@click.option('-p', '--port', prompt='Port number', type=int, help='The port number to fuzz.')
@click.option('-s', '--step', default=100, help='Step size for fuzzing.')
@click.option('-t', '--timeout', default=5, help='Timeout for the connection.')
@click.option('--verbose', is_flag=True, help='Enable verbose output.')
@click.option('--debug', is_flag=True, help='Enable debug output.')
def main(ip: str, port: str, step: int, timeout: int, verbose: bool, debug: bool): # noqa E501
click.echo("[+] Fuzzing to OVERFLOW")
for counter in range(step, 30 * step, step):
payload = "A" * counter + '\n'
payload = payload.encode('utf-8')
s = remote(ip, port)
try:
r = s.recv(1024, timeout=timeout)
if debug:
click.echo(r)
# pre function
pre(s, timeout)
if debug:
click.echo(r)
# send payload
click.echo("Fuzzing with %s bytes" % len(payload))
s.send(payload)
r = s.recv(1024, timeout=timeout)
if verbose or debug:
click.echo(r)
# post function
post(s, timeout)
if debug:
click.echo(r)
except Exception as e:
click.echo("Could not connect to " + ip + ":" + str(port))
click.echo("Error: " + str(e), err=True)
sys.exit(0)
finally:
s.close()
time.sleep(1)
if __name__ == '__main__':
main()