문제 설명
드멍이는 가을학기 중간고사를 위해 열심히 공부 중이에요.
상대성 이론이 이해가 되지 않아 드냥이에게 물어보니, 이 문제를 풀어오면 도와주겠다고 하네요.
문제를 풀고 드멍이의 중간고사 점수를 지켜주세요!
Files
ubuntu@WSL2:~/CTF/dreamhack.io$ tree Theori\ of\ Relativity Theori of Relativity └── relativity 0 directories, 1 file ubuntu@WSL2:~/CTF/dreamhack.io$ file Theori\ of\ Relativity/relativity Theori of Relativity/relativity: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f525968be1ff734ca35e46f700fbc3f84df12440, for GNU/Linux 3.2.0, stripped
64비트 리눅스용 실행 파일 하나.
분석
main
__int64 __fastcall main(int a1, char **a2, char **a3) { char s[104]; // [rsp+0h] [rbp-70h] BYREF unsigned __int64 v5; // [rsp+68h] [rbp-8h] v5 = __readfsqword(0x28u); printf("Key: "); fgets(s, 100, stdin); s[strcspn(s, "\n")] = 0; if ( sub_55B09C06D298(byte_55B09C070020, (unsigned __int8 *)s) ) printf("Congrats! Flag is DH{%s} ...if you didn't cheat!\n", s); else puts("Hmm... :thinking_face:"); return 0LL; }
- 사용자로부터 \n 개행문자를 포함한 Key값을 100바이트만큼 입력받아 s에 넣는다.
이때 \n 개행문자는 엔터키를 눌렀을때 자동으로 포함된다. - s에서 \n 개행문자가 들어간 위치에 \n대신 0을 넣는다.
- byte_55B09C070020와 s를 매개변수로 지정하여 sub_55B09C06D298 함수를 호출했을 때 참이면, Flag를 출력한다.
sub_55B09C06D298
bool __fastcall sub_55B09C06D298(_BYTE *a1, unsigned __int8 *a2) { unsigned __int8 v5; // [rsp+12h] [rbp-2Eh] unsigned __int8 v6; // [rsp+13h] [rbp-2Dh] char i; // [rsp+18h] [rbp-28h] __int128 v8; // [rsp+20h] [rbp-20h] v8 = 0uLL; for ( i = 0; ; ++i ) { v5 = *a1 + i * i; v6 = *a2; if ( !__popcnt(v5) ) break; if ( (i & 1) != 0 ) v8 += (unsigned __int8)(v6 ^ v5); else v8 += (unsigned __int8)(v5 - v6); ++a1; ++a2; } return (int)__popcnt(v6) + v8 == 0; }
popcnt?
“population count”라고도 하는데,
레지스터나 메모리 위치에 있는 값을 이진수로 변환하여 1의 개수를 세는 어셈블리 명령이다.
이를 테면 __popcnt(v5)에서 v5가 3이면,
3을 2진수로 변환했을때 0b11, 즉 1이 2개 있으므로 2가 된다.
v5가 255라면,
255를 2진수로 변환했을때 0b11111111, 즉 1이 8개 있으므로 8이 된다.
따라서 이 함수가 작동되는 원리를 살펴보자면,
각각 매개변수로 나타내는 a1은 byte_55781E4DA020,
a2는 사용자가 입력한 flag, 그러니까 main에서 입력받았던 Key인 s를 가리킨다.
- 루프문 안에 v5는 (byte_55781E4DA020[i] + i * i) & 255가 된다.
마지막에 255를 and 연산하는 이유는 unsigned __int8 타입이기 때문이다. - 루프문 안에 v6는 사용자로부터 입력받은 flag[i]가 된다.
- __popcnt(v5)가 0일때 루프문에서 빠져나온다. 즉 v5가 0일때 빠져나오게 된다.
- 반복 카운트인 i가 홀수이면, v8 += (unsigned __int8)(v6 ^ v5);
반대로 0 또는 짝수이면, v8 += (unsigned __int8)(v5 – v6); - byte_55781E4DA020와 flag 문자열의 인덱스를 각각 늘리고 루프문으로 돌아간다.
- 만약 루프문을 빠져나오게 되면, __popcnt(v6) + v8 값이 0이 되어야 참을 반환한다.
결론적으로 각각 __popcnt(v6)가 0, v8을 0으로 만족시켜야 되는데,
4번 과정에서 v6와 v5 값을 서로 같게 만들면 쉽게 해결할 수 있는 문제였다.
그전에 앞서 byte_55781E4DA020를 추출시켜야할 필요가 있는데,
이게 실행시킬때마다 무슨 이유에선지 바뀐다.
—————————————————
+P.S) ELF RELA Relocation Table을 참고할 것 (0x00055B09C06A628)
—————————————————
gdb 디버깅을 시도하면,
__int64 sub_55B09C06D189() { if ( ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL) >= 0 ) return (unsigned int)(unsigned __int8)byte_55B09C070020[0]-- - 1; else return (unsigned int)(unsigned __int8)byte_55B09C070020[0]++ + 1; }
ptrace 함수를 통해 디버깅을 감지해서 byte_55B09C070020 데이터가 또 바뀌기 때문에
그냥 후킹 라이브러리를 따로 만들어서 추출하기로 했다.
//gcc -shared -fPIC -o hook_puts.so hook_puts.c -ldl //LD_PRELOAD=./hook_puts.so ./relativity #define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <string.h> #include <stdint.h> typedef int (*org_puts)(const char *str); int puts(const char *str) { char new_str[1024]; org_puts original_puts; original_puts = (org_puts) dlsym(RTLD_NEXT, "puts"); printf("hooked str: %s\n", str); strcpy(new_str, str); uint64_t main_address = 0x555555557384 + 0x2c9c; //main address + (main ~ byte_55B09C070020간 거리) size_t num_bytes = 0; uint8_t buffer[1024]; // 메모리 주소에서 바이트 단위로 읽어오기 for (size_t i = 0; ; i++) { if(*((uint8_t *)(main_address + i)) == 0xFF) break; buffer[i] = *((uint8_t *)(main_address + i)); num_bytes++; } for (size_t i = 0; i < num_bytes; i++) { printf("0x%02x ", buffer[i]); } printf("\n"); return original_puts(new_str); }
위 코드는 Hmm… :thinking_face: 문구를 출력시키는 puts함수에 후킹을 거는 코드를 작성하고
후킹건 함수에서 byte_55B09C070020에 담긴 데이터를 덤프시킨다.
ASLR을 비활성화하고, LD_PRELOAD 환경변수를 통해서 위 코드로 만든 라이브러리를 로드시키면 된다.
ubuntu@WSL2:~/CTF/dreamhack.io/Theori of Relativity$ gcc -shared -fPIC -o hook_puts.so hook_puts.c -ldl ubuntu@WSL2:~/CTF/dreamhack.io/Theori of Relativity$ sudo sysctl kernel.randomize_va_space=0 kernel.randomize_va_space = 0 ubuntu@WSL2:~/CTF/dreamhack.io/Theori of Relativity$ LD_PRELOAD=./hook_puts.so ./relativity Key: aaaaaaaaaaaaaaaaa hooked str: Hmm... :thinking_face: 0x31 0x63 0x30 0x2c 0x29 0x4d 0x3e 0x33 0x24 0xe7 0xd1 0xbf 0xa0 0x8a 0xa1 0x57 0x32 0x11 0xef 0xfa 0xa2 0xa8 0x4d 0x20 0xf6 0xbf 0x90 0x8b 0x51 0x1a 0xb3 0x71 0x38 0x20 0xdf 0x9c 0x51 0x0d 0xc2 0x71 0x23 0xa3 0x51 0xfb 0xd1 0x7c 0x20 0xc2 0x65 0xd4 0x72 0x39 0xa1 0x68 0xd4 0x93 0xf4 0xb4 0x0c 0x9d 0x23 0xaa 0x32 0xaf 0x00 0x7f 0xfc 0x77 0xf0 0x67 0xdc 0x4f 0xc0 0x2f 0x9c 0x07 0x70 0xd7 0x3c 0x9f 0x00 0x5f 0xbc 0x17 0x70 0xc7 0x1c 0x6f 0xc0 0x0f 0x5c 0xa7 0xf0 0x37 0x7c 0xbf 0x00 0x3f 0x7c 0xb7 0x00 0x00 0x00 Hmm... :thinking_face:
char byte_55B09C070020[104] 31 63 30 2c 29 4d 3e 33 24 e7 d1 bf a0 8a a1 57 32 11 ef fa a2 a8 4d 20 f6 bf 90 8b 51 1a b3 71 38 20 df 9c 51 0d c2 71 23 a3 51 fb d1 7c 20 c2 65 d4 72 39 a1 68 d4 93 f4 b4 0c 9d 23 aa 32 af 00 7f fc 77 f0 67 dc 4f c0 2f 9c 07 70 d7 3c 9f 00 5f bc 17 70 c7 1c 6f c0 0f 5c a7 f0 37 7c bf 00 3f 7c b7 00 00 00 ff
Python3로 구현시킨 sub_55B09C06D298 함수
decrypted_byte_55781E4DA020 = [0x31, 0x63, 0x30, 0x2C, 0x29, 0x4D, 0x3E, 0x33,\ 0x24, 0xE7, 0xD1, 0xBF, 0xA0, 0x8A, 0xA1, 0x57,\ 0x32, 0x11, 0xEF, 0xFA, 0xA2, 0xA8, 0x4D, 0x20,\ 0xF6, 0xBF, 0x90, 0x8B, 0x51, 0x1A, 0xB3, 0x71,\ 0x38, 0x20, 0xDF, 0x9C, 0x51, 0x0D, 0xC2, 0x71,\ 0x23, 0xA3, 0x51, 0xFB, 0xD1, 0x7C, 0x20, 0xC2,\ 0x65, 0xD4, 0x72, 0x39, 0xA1, 0x68, 0xD4, 0x93,\ 0xF4, 0xB4, 0x0C, 0x9D, 0x23, 0xAA, 0x32, 0xAF,\ 0x00, 0x7F, 0xFC, 0x77, 0xF0, 0x67, 0xDC, 0x4F,\ 0xC0, 0x2F, 0x9C, 0x07, 0x70, 0xD7, 0x3C, 0x9F,\ 0x00, 0x5F, 0xBC, 0x17, 0x70, 0xC7, 0x1C, 0x6F,\ 0xC0, 0x0F, 0x5C, 0xA7, 0xF0, 0x37, 0x7C, 0xBF,\ 0x00, 0x3F, 0x7C, 0xB7, 0x00, 0x00, 0x00] def popcnt(n): return bin(n).count('1') # my_flag = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKL" my_flag = "1d459fbdd85803e8223c2a11604dac728aceaffbc454aedce56b1a8d4e063360" my_flag = [ord(char) for char in my_flag] my_flag.append(0) # print(my_flag) val = 0 v8 = 0 i = 0 tmp = 0 sum_tmp = 0 guessed_flag = [] while True: # print(i) v5 = (decrypted_byte_55781E4DA020[i] + i * i) & 255 v6 = my_flag[i] if(popcnt(v5) == 0): # print(i) -> 64, always # print(decrypted_byte_55781E4DA020[i]) -> 0, always # print(v5) -> 0, always print(f"[FINAL] v5 = {v5}, v8 = {v8}, v6 = {v6}") val = popcnt(v6) + v8 break if i & 1 != 0: print(f"[A] v5 = {v5}, v8 = {v8}, v6 = {v6}, tmp = {tmp}") tmp = ((v6 ^ v5) & 255) v8 = v8 + tmp print(f"[a] v5 = {v5}, v8 = {v8}, v6 = {v6}, tmp = {tmp}") guessed_flag.append(v5) else: print(f"[B] v5 = {v5}, v8 = {v8}, v6 = {v6}, tmp = {tmp}") tmp = ((v5 - v6) & 255) #tmp + v6 = v5, v6 = v5-tmp v8 = v8 + tmp print(f"[b] v5 = {v5}, v8 = {v8}, v6 = {v6}, tmp = {tmp}") guessed_flag.append(v5) i+=1 # print(hex(val)) print(f"val = {hex(val)}, {val}") #should return 0
Solution
decrypted_byte_55781E4DA020 = [0x31, 0x63, 0x30, 0x2C, 0x29, 0x4D, 0x3E, 0x33,\ 0x24, 0xE7, 0xD1, 0xBF, 0xA0, 0x8A, 0xA1, 0x57,\ 0x32, 0x11, 0xEF, 0xFA, 0xA2, 0xA8, 0x4D, 0x20,\ 0xF6, 0xBF, 0x90, 0x8B, 0x51, 0x1A, 0xB3, 0x71,\ 0x38, 0x20, 0xDF, 0x9C, 0x51, 0x0D, 0xC2, 0x71,\ 0x23, 0xA3, 0x51, 0xFB, 0xD1, 0x7C, 0x20, 0xC2,\ 0x65, 0xD4, 0x72, 0x39, 0xA1, 0x68, 0xD4, 0x93,\ 0xF4, 0xB4, 0x0C, 0x9D, 0x23, 0xAA, 0x32, 0xAF,\ 0x00, 0x7F, 0xFC, 0x77, 0xF0, 0x67, 0xDC, 0x4F,\ 0xC0, 0x2F, 0x9C, 0x07, 0x70, 0xD7, 0x3C, 0x9F,\ 0x00, 0x5F, 0xBC, 0x17, 0x70, 0xC7, 0x1C, 0x6F,\ 0xC0, 0x0F, 0x5C, 0xA7, 0xF0, 0x37, 0x7C, 0xBF,\ 0x00, 0x3F, 0x7C, 0xB7, 0x00, 0x00, 0x00] def popcnt(n): return bin(n).count('1') i = 0 flag = [] while True: v5 = (decrypted_byte_55781E4DA020[i] + i * i) & 255 if(popcnt(v5) == 0): break if i & 1 != 0: flag.append(v5) else: flag.append(v5) i+=1 for i in range(len(flag)): print(chr(flag[i]), end='')
FLAG
ubuntu@WSL2:~/CTF/dreamhack.io/Theori of Relativity$ ./relativity Key: 1d459fbdd85803e8223c2a11604dac728aceaffbc454aedce56b1a8d4e063360 Congrats! Flag is DH{1d459fbdd85803e8223c2a11604dac728aceaffbc454aedce56b1a8d4e063360} …if you didn't cheat!
DH{1d459fbdd85803e8223c2a11604dac728aceaffbc454aedce56b1a8d4e063360}