문제에서 주어지는 소스코드는 아래와같다. (C 코드)
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#define FAKEUID 1000
int main(int argc, char **argv, char **envp)
{
int c;
char token[256];
if(getuid() != FAKEUID) {
printf("Security failure detected. UID %d started us, we expect %d\n", getuid(), FAKEUID);
printf("The system administrators will be notified of this violation\n");
exit(EXIT_FAILURE);
}
// snip, sorry :)
printf("your token is %s\n", token);
}
소스코드의 흐름을 해석하면 아래와 같다.
1. FAKEUID 이름의 전역변수의 값을 1000으로 설정한다.
2. 메인함수가 싫행되는데, token 문자배열이 256 바이트크키만큼 할당당된다.
3. getuid() 로 현재 uid 값이 1000 과 같은지 비교한다.
4. 그 결과에따라 흐름이바뀌는데, uid 가 1000 과 일치하면 token 이 출력된다.
flag13 사용자 디렉터리에는 seuid 가 설정된된 리눅스 실행파일이 있다. 사실 이번문제는 gdb 로 디버깅을 하면된다.
아래와같이 gdb 로 실행파일을 열어보면, 실행파일이 동작하는 실행흐름을 볼 수 있다.
소스코드에서는 if 절로 현재 uid 와 FAKEUID 값인 1000 을 비교하고있는데, gdb 에서 getuid() 의 값을 1000으로 변경시켜주면 간단하게 해결된다.
if(getuid() != FAKEUID) {
printf("Security failure detected. UID %d started us, we expect %d\n", getuid(), FAKEUID);
printf("The system administrators will be notified of this violation\n");
exit(EXIT_FAILURE);
}
// snip, sorry :)
printf("your token is %s\n", token);
소스코드에서 if 절로여기서 uid 값이 1000 과 일치하는지 비교하는구문은 main+48 위치이다.
0x3e8 은 10진수로 1000을 의미한다. 즉, 레지스터 eax 값이 1000 이어야한다는 소리이다.
mian+48 지점에 breakpoint 를 걸고 프로그램을 실행시키게되면 main+48 부분에서 걸리게된다.
상단의 사진과 다르게 => 표시가 현재 지점이란 표시이다.
현재사용자는 level13 이고 level13 의 uid 는 1014 이기떄문에 getuid() 함수의 리턴값이 담긴 레지스터 eax 값이 1014임을 확인할 수 있다. 이상태로 continue 를하면 uid 가 일치하지않아 종료된다.
하지만, 이 eax 를 1000으로 강제로 변조시켜주면 uid가 1000으로 인식되어 if 분기절을 뛰어넘고 token 값을 얻어 해당문제를 clear 할수 있게된다. eax 값을 변조하기 전의 값이 1014 임을 확인하자.
아래와같이 set 으로 eax 레지스터 의 값을 1000 으로변조시켜주고 레지스터값을 확인해보면 1000으로 바뀐것을 확인할 수 있다.
이제 이 상태로 continue 시켜주면 token 값을 얻을 수 있다.
이제 해당 token 값으로 flag13 계정으로 로그인해서 getflag 를 외쳐주면 해당문제가 클리어된다.