2021.01.05


W3Challs - First steps on Linux (Reversing, 10pts)


[그림 1] 프로그램 실행 화면


이번 문제는 리눅스의 바이너리 파일인 ELF 파일을 리버싱하는 문제이다. Kali Linux를 이용해 프로그램을 실행해봤는데, 역시나 문자열 입력을 받고 그에 따른 응답을 출력하는 형태이다. 우선 기드라에서 어떻게 돌아가는지 확인해보았다.

[그림 2] Main 함수 중 일부


Defined Strings를 이용해서 대충 main 함수로 보이는 녀석을 찾았다. [그림 2]에 나타난 부분이 입력값을 검증하는 부분이다. 입력값을 바이트 단위로 가져와 특정 데이터와 XOR 연산을 하는데, 그 값이 0이 되어야 한다. 해당 데이터들은 역시나 프로그램 내 있으므로 그냥 가져와서 아래와 같이 간단한 코드를 짜면 해결…인데?

import string

array = [0x26, 0xCF, 0x92, 0x11, 0x09, 0x1C, 0xAB, 0x88, 0x0D, 0x7C, 0xBB,
         0x1D, 0x2D, 0x84, 0xC8, 0x52, 0x3F, 0x74, 0x84, 0x85, 0x25, 0x5D,
         0xDD, 0x39, 0x3A, 0x80, 0xEB, 0x5C, 0x19, 0x00, 0x00, 0x00]

flag = ''

for i in range(0x1d):
    for a in string.printable:
        if ord(a) ^ array[i] == 0:
            flag += a

print(flag)

[그림 3] 해치웠나?


완전 이상한 값이 튀어나와버린다. 당연히 저 값은 우리가 찾는 플래그가 아니다. 사실 데이터셋을 볼 때부터 뭔가 이상했는데, 어떤 값과 XOR을 해서 0이 되는 값은 그 값과 동일한 값이기 때문이다. 즉, 사실상 저 위의 데이터셋들이 곧 플래그인 셈인데 위에 있는 값들 중 상당수가 일반적인 ASCII 코드와 맞지 않는다. [그림 2]에 있는 코드만으로는 이 원인을 알 수 없으므로, 다른 함수들도 둘러보기로 했다.

[그림 4] 데이터 변경 루틴


아니나 다를까 역시나 기존 데이터의 값을 변경하는 루틴이 들어있었다. 이것도 별도로 구현을 해볼까 했으나 뭔가 이상해서 다른 방법을 사용하기로 했다. 디버거로 변경된 데이터셋을 확인하는 것인데, 이 파일은 ELF 파일이니 gdb를 사용하였다.

그래서 gdb로 프로그램을 열어서 입력값과 데이터셋을 XOR하는 부분에 bp를 걸고 실행했는데, 입력을 받기도 전에 종료시켜버린다. 이 프로그램이 아무런 심볼도 없어서 gdb로는 원인을 분석하기 쉽지 않았다. 그래서 다시 기드라로 돌아가서 어디서 막히는지 살펴봤다.

알고 보니 [그림 4]에도 보이던 if문 위에 코드가 디버깅을 감지하는 녀석들이었다. 해당 부분을 어셈블리로 보면 아래와 같다.

MOV EAX, 0x1A
INT 0x80
CMP EAX, -0x1

INT 0x80은 리눅스에서 System Call을 의미한다. 이 때 EAX에 있는 값에 따라 호출할 함수가 정해지는데, 0x1A는 ptrace를 의미한다. (32bit 환경에 한함) 그리고 이 ptrace에 의해 디버깅이 바로 종료되었던 것이다.

이제 디버깅이 막힌 원인을 알았으니 플래그를 얻는 일만 남았다.

gdb에서 CMP EAX, -0x1 부분에 bp를 걸고 EAX를 -1이 아닌 값으로 변경해주면 바로 우회할 수 있다. 제대로 우회했다면 입력창이 나올 것이고, 아무 값이나 입력을 하면 아까 걸어놓았던 첫번째 bp에서 멈추게 된다. 해당 시점에서는 데이터셋이 변경된 상태이므로 그냥 값을 확인하면 된다.

[그림 5] 변경된 데이터셋


역시나 ASCII 코드로 잘 변경될 것 같은 값들만 모여있는 것을 볼 수 있다. 이 값들을 그대로 가져와서 문자열로 변형해주면 플래그를 얻을 수 있다. 해당 값들은 Little endian 형식임을 유의할 것.

이제 가져온 값들을 이용해 아래와 같이 간단한 코드를 짜면 플래그가 나올 것이다.

import string

array = [0x70, 0x54, 0x72, 0x34, 0x63, 0x33, 0x5f, 0x31, 0x73, 0x5f,
         0x53, 0x30, 0x5f, 0x33, 0x34, 0x73, 0x59, 0x5f, 0x74, 0x30,
         0x5f, 0x62, 0x59, 0x70, 0x34, 0x73, 0x53, 0x21, 0x21]

flag = ''

for a in array:
    flag += chr(a)

print(flag)

[그림 6] 플래그


[그림 7] 풀이 완료!


그렇게 얻은 플래그를 W3Challs에 가서 입력하면 10점을 얻을 수 있다. 플래그에도 ptrace가 언급되는 것을 보아 대충 의도에 맞게 풀었나보다..