리버싱 문제가 몇 나왔었는데 제 실력으로는 1문제 겨우 풀었습니다...
1. Fishing

파일을 다운로드한 후, 프로그램을 실행하면 다음과 같은 실행창이 열립니다.

입력값으로 플래그 자체를 입력하는 듯한 문제입니다.
이 파일을 IDA로 디컴파일 하는데 디컴파일 결과가 좀 이상합니다.
정적분석만으로는 한계가 있는 듯 하여 디버깅을 하면서 프로그램을 분석하겠습니다. 또 하나 특이점으로는 안티 디버깅이 걸려있어 이 루틴을 우회해야합니다. 안티 디버깅 우회부터 찾아봅시다.
디버깅을 진행하다 보면 다음과 같은 명령어를 찾을 수 있습니다. 해당 명령어는 디컴파일 결과의 sub_24CF() 함수 중 일부입니다.

루프를 총 4번 도는 명령어입니다. 첫 번째 rax 호출에는 안티디버깅이 걸려있지 않으니 두번째 rax 호출 함수를 확인합시다.
함수를 진행하다 보면 이상한 명령어들이 있습니다.

그것은 위와 같은 jmp 명령문인데 기계어를 보면 jmp offset이 0xFF(-1)로 되어있습니다. 따라서 0x7FF7EBE818DE 명령어를 한번 실행하면 다음 사진처럼 명령어가 진행되는 것을 알 수 있습니다. 새로운 명령어인 FFC3(inc ebx)로 인식하고, 그 다음 jmp 명령어를 수행하면 FFCB(dec ebx)로 인식하여 결과적으로는 아무 행동도 하지 않습니다. 디컴파일 결과가 이상한 것을 이것이 원인이라 생각합니다. 이러한 명령어들은 프로그램 곳곳에 존재합니다.

각설하고 2번째 rax 함수 호출에서는 Anti Debugging 분기가 3군데 있으며 J 조건문을 전부 반대로 패치해주시면 됩니다.


3번째 rax 함수 호출에서도 Anti Debugging 분기가 1군데 있으며 이를 패치해주시면 됩니다.

이제 안티 디버깅 문제는 해결했으니 본격적으로 프로그램을 분석해봅시다.
프로그램을 계속 진행하면 입력 이후 createThread함수를 호출합니다. ThreadProc는 0x7FF7BE461E2F인 듯 하니 여기에 BP를 걸고 실행시켜봅시다.

쓰레드 함수를 진행 시키면 입력값을 매개변수로 주고 여러 함수들을 호출하는 것을 볼 수 있습니다.

위 함수를 Step into(F7)를 하면 함수코드가 나오는데 이를 요약하면 각 입력값을 xor 연산해줍니다. 이 값을 data_enc1이라고 합시다.

두 번째 함수가 조금 특이한데, 두 번째 함수를 Step into(F7)한 이후 더이상 F7, F8로 실행이 안되고 Step into 대신 Step over(F8)을 하면 두 번째 함수 이전에 다른 곳으로 점프합니다(원인은 아직도 모르겠습니다). 이 곳의 명령어를 전부 실행한 이후에 두 번째 함수로 돌아가는데 문제는 이 과정에서 data_enc1 값이 바뀝니다. 그 과정을 요약하면 다음과 같습니다.
for(int i = 0; i < len(data_enc1); i++){
data_enc1[i] = (data_enc1[i] << 3) | (data_enc[i] >> 5);
}
이 과정을 거친 후 두 번째 함수로 진입하고, 두 번째 함수를 요약하면 각 입력값에 sub 0x22를 합니다. 이 값을 data_enc2라고 합시다.

그 다음 마지막 암호화 함수를 호출하고 memcmp를 통해 암호화된 플래그와 특정 데이터를 비교합니다.
마지막 암호화 함수는 디컴파일 결과가 정상으로 나와 이를 참고하겠습니다.

여기서 매개변수 a1이 data_enc2에 해당하고 그 결과를 a5(malloc 한 Heap영역의 주소)에 저장합니다.
sub_140002230()함수는 v7에 256바이트 만큼 데이터를 저장하는데, 이 값은 항상 일정하기 때문에 따로 확인하진 않겠습니다.
전체적인 프로그램 흐름을 요약하면 다음과 같습니다.
1. 입력값 받기
2. data_enc1[i] = data[i] ^ 0x21
3. data_enc1[i] = (data_enc1[i] << 3) | (data_enc1[i] >> 5)
4. data_enc2[i] = data_enc1[i] - 0x22
5. data_enc3[i] = (v11 - 24) ^ v8 ^ data_enc2[i]
6. memcmp(data_enc3, answer)
answer 값은 프로그램 내에 있으므로 이를 통해 암호화한 것을 복호화 하면 됩니다. 다만 3번에서 복호화가 불가능하다고 판단해 4번까지 복호화한 결과와 0 ~ 255 의 값을 1-3번 암호화한 값과 비교하는 식으로 정답을 찾아내는 코드를 작성하였습니다.
+) 지금 생각해보니 (data_enc[i] >> 3) | (data_enc1[i] << 5)를 이용하면 복호화가 가능하네요. 당시엔 왜 이 생각을 못했지..
프로그램 결과

이 값을 입력해주면 정답으로 인식합니다.

사실 이 풀이 말고 다른 정석 풀이가 있을 것 같은데.. 전 디버깅만 할 줄 아는 놈이라 무식(?)하게 프로그램 분석했습니다.
'CTF' 카테고리의 다른 글
| [CTF] CCE 2024 Qual Write-up (0) | 2024.08.13 |
|---|---|
| [CTF] Codegate 2024 Write-up (0) | 2024.06.02 |
| [CTF] wolvCTF (0) | 2023.03.20 |