[RC3-CTF] loginme

해킹대회 문제 풀이 연습장소

Moderator: amesianx

Post Reply
swoo1013
Posts: 31
Joined: Tue Sep 20, 2016 4:25 pm

[RC3-CTF] loginme

Post by swoo1013 » Sun Dec 11, 2016 10:48 pm

간단한 리버싱 문제이며, 한번 풀어보도록 하겠습니다.

우선 file 명령어로 해당 바이너리의 정보를 확인해볼게요.
확인해보면 64비트 환경에서 돌아가는 파일임을 알 수 있고, 심봉정보가 stripped 된점을 살펴보면,
함수 정보들을 삭제했을 것으로 보이네요. 그럼 ida로 까보도록 하죠
캡처.PNG
캡처.PNG (4.18 KiB) Viewed 367 times
아래 그림을 보면 main 함수를 ida는 아주 가볍게 찾아주네요
역시 갓ida.... ida를 이용해서 문제를 풀 수 있을 것으로 보이는데요, main 함수 내부로 들어가서 확인해볼게요
캡처.PNG
캡처.PNG (9.73 KiB) Viewed 367 times
main 함수로 들어와서 ida의 주력인 헥스레이를 이용해서 code를 디버깅해볼게요
확인해보면 strcpy(buffer, :\"AL_RT^L*.?+6/46); 이 값이 보이는데, strcpy 함수를 이용해서 알 수 없는 스트링을 buffer 변수에 저장을 하네요
그리고 v6변수에는 7을 대입을 하네요. 여기까지는 간단하네요. 그리고 printf 함수로 뭐 패스워드를 입력하라는 문제같은데요
패스워드 비교하는 if문이 보이는데 이는 gdb를 이용해서 까보면 간단히 해결할 수 있고, 파이썬 코딩을 이용해서 문제를 해결할 수 있을 것으로 보이네요
캡처.PNG
캡처.PNG (20.36 KiB) Viewed 367 times
일단 뭐 게속해서 쭉 내려가보면 심볼이 사라져잇는 상태여서 어떤 함수가 어떤 역할을 하는지는 직접 들어가서 확인을 해봐야 겠네요.
아래의 그림을 보면 조건식에 일치하지 않으면 incorrect를 출력하면서 비밀번호가 틀렸다고 출력하라고 코딩을 해놨네요
우선 이 조건식을 만족하기 위해선 위의 그림처럼 if (v3 < strlen(buffer)인데, v3는 사용자의 입력값인 scanf 함수로 input을 받아오네요 즉
사용자의 입력값과 buffer에 저장된 저 의미심장한 스트링의 길이가 일치하지않으면 1차적으로 return_false함수로 분기를 하네요(return_false)는 제가 직접 함수 name을 보기쉽게 변경한겁니다.
캡처.PNG
캡처.PNG (1.85 KiB) Viewed 367 times
if ( v3 < strlen(buffer) ) // 사용자의 입력값이 버퍼의 길이인 17보다 작으면 return_false!
return_false();
for ( i = 0; i < strlen(s); ++i )
{
if ( i >= strlen(buffer) ) // 사용자의 입력값보다 버퍼의 길이가 더 작으면 return_false
return_false();
if ( s != (*(&xor_key + i % v6) ^ buffer) )// 최종 xor연산!
return_false();
}
sub_4007F0();
}

그리고 추가로 2번 더 조건식을 참조하는데 이는 xor 연산을 위해서 사용을 합니다. 사용자의 입력값만큼 for문에 길이만큼 대입을 합니다.
즉 정상적인 답이 출력되기 위해선 사용자의 입력값과 strcpy로 복사한 스트링 길이가 일치해야지만 정답을 출력할 것으로 보이네요
아.. 그리고 참고로 저 strcpy는 원래 18바이트가 정상적인데 2번째 문자열이 \로 나타나있는데, 이는 특수문자 처리를 위해서 사용하는 문자열로
해당 문제에서 아마도 fake성으로 작성을 한 거 같네요 저도 처음엔. 18바이트인줄 알았는데, 알고보니 \"로 되있길래 \"는 결국 1개의 문자로 인식을
하게 됩니다.

그리고 마지막으로 s != (사용자의 입력값에서 각각의 오프셋의 값이) xor_key + i % v6) ^ buffer라고 나타나있는데, 여기서 조금 해맸네요.
사용자가 입력한 오프셋이랑 &xor_key + i % v6(7)는 xor_key는 harambe라는 값으로 rodata 섹션에 저장되 있으며, 읽기전용 스트링입니다.
즉 xor_key가 저장된 rodata 섹션의 주소값에서 +i(사용자의 입력 값 길이만큼) 주소를 더한 값에서 7로 나눴을 때 나머지의 값이 사용자의 입력값과 동일한지 비교를 하는 것으로 보입니다... 즉 buffer 변수는 총 8바이트 할당이 되어 있기 때문에, 결국 일반적인 루트로 계산을 했을 땐 8바이트만 답이 출력될 겁니다. 이유는 방금 설명했듯이 사용자의 입력값만큼 strlen함수로 길이를 받아오는데, 이 길이는 buffer 할당된 크기보다 크기 때문에 답이 제대로 출력되지 않았고, xor 최종 연산에도 정상적으로 flag가 출력되지 않는다는 겁니다.

얘기가 길어질거 같으니 문제를 한번 풀어보죠.
실행을 하고 아무 입력값을 넣으면 ida에서 분석한대로 incorrect를 뱉어주네요.
[attachment=1]캡처.PNG[/attachment]

peda-gdb를 이용해서 문제를 해결해보겠습니다. 쉽게 생각해보면 문자열의 길이만 맞추면 1,2번째 statement에는 만족하기 때문에 최종적으로 xor 연산에서 비교하는 값을 분석하면 될 것으로 보이네요.
최종 xor 연산 구문에서 확인해보면 사용자의 입력값은 edx, rbp+rsi+s에 의해서 edx 레지스터에 저장하고,
xor 연산을 이용해 출력된 최종 연산 값을 cmp edx, edi로 비교해서 분기를 하는 것으로 보이네요 그럼 저희는 여기서 사용자의 입력값이 아닌
정적 rodata 섹션에 저장된 의미심장한 스트링 값을 확인해보면 되겠네요
[attachment=0]캡처.PNG[/attachment]
우선 브레이크 포인트를 지정할 건데요, 문자열 17자리만 맞춰서 입력해준 뒤, 마지막 최종연산만 보면 되기 때문에, ida에서 확인한
0x40078C라는 위치에 브레이크 포인트를 걸었어요... 확인해보면 예상했던대로 RDX와, RDI(R이 붙은건 64비트 레지스터 표현 방식입니다) 레지스터에서는 사용자 입력값에서 처음 입력한 0x31이 RDX에 저장됬고, XOR 최종 연산된 FLAG 값은 0x52 R이 저장되있네요.

Breakpoint 1, 0x000000000040078c in ?? ()
gdb-peda$ set $RDX=0x52
gdb-peda$ i r
rax 0x0 0x0
rbx 0x0 0x0
rcx 0x52 0x52
rdx 0x31 0x31
rsi 0x0 0x0
rdi 0x52 0x52

우선 답을 출력하기 위해선 RDX에 제가 입력한 값을 변조해야되기 떄문에 set 명령어로 0x52로 변경한 뒤 c 명령어로 진행해보겠습니다
gdb-peda$ c
Continuing.
[----------------------------------------------------registers-----------------------------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x43 (b'C')
RDX: 0x32 (b'2')
RSI: 0x1
RDI: 0x43 (b'C')

계속해서 값을 변조해서 확인해보면 RC3-2016-XORISGUD이란느 FLAG가 출력되면서 CORRECT 결과 값을 확인할 수 있습니다.
이 방법외에도 파이선으로 푸는 방법도 있는데 그 방법은 다음에 포스팅하도록 할게요~
Attachments
캡처.PNG
캡처.PNG (15.06 KiB) Viewed 366 times
캡처.PNG
캡처.PNG (3.01 KiB) Viewed 366 times

Post Reply

Who is online

Users browsing this forum: No registered users and 17 guests