1. 문제
1) mitigation 확인
보호기법이 다걸려있다.
2) 문제 확인
바이너리를 실행시키면 입력을 한번 받고 그대로 종료하게 된다.
3) 코드흐름 파악
•
initialize() 함수
__int64 initialize()
{
__int64 result; // rax
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
fd = open("flag.txt", 0);
result = (unsigned int)fd;
if ( fd < 0 )
{
puts("flag.txt not found");
exit(1);
}
return result;
}
C
복사
메인함수에 초기화 관련된 함수가 있다. 플래그 파일을 open한다. 따라서 로컬상에서 테스트용으로 flag.txt 파일을 하나 만들어 줬다.
•
vuln() 함수
void __noreturn vuln()
{
__int64 v0; // [rsp-210h] [rbp-210h]
__int64 v1; // [rsp-110h] [rbp-110h]
unsigned __int64 v2; // [rsp-8h] [rbp-8h]
v2 = __readfsqword(0x28u);
read(0, &v0, 0x100uLL);
snprintf((char *)&v1, 0x100uLL, (const char *)&v0);
exit(0);
}
C
복사
0x100 만큼 v0에다가 입력을 받는다. 그다음 snprintf로 v1에다가 출력을 하는데, fsb가 여기서 터진다. 또한 바이너리에 win 함수가 주어진다. fsb를 이용해서 win 함수를 호출하면 플래그는 따질것이다.
2. 접근방법
문제 설명에서 매우 쉽다고 써져있었는데 4시간동안 삽질하다가 결국 못풀었다. 팀원이 풀긴했는데, 익스 코드를 봐도 모르겠어서 일단 대회 당일에는 넘기고, 대회가 끝난뒤에 다시 천천히 분석을 해보았다.
snprintf가 호출되기 직전의 스택 상황은 다음과 같다
fsb가 가능하니까 스택에 쓸수있는 주소를 적당히 가리키고 있는 부분이 뭐가 있을까 처음에 찾아봤다. 근데, 해당 문제에는 모든 보호기법이 다걸려있기도 하고, 정상 종료가 아닌, exit함수로 종료된다. 따라서 exit got를 덮거나, fini_array, 아니면 exit 내부에서 free나 malloc_hook을 덮으면 될것같았다.
하지만 주소 leak이 되야하는데, 스택 상황을 봐서는 leak을 할 수 있는 벡터가 전혀 보이지 않았다;;
뭐 어쨋든, 위 사진을 보면 0x..a10 주소에 0x..a44를 가리키고 있는 주소가 보인다. 저 위치는 %12$n
으로 접근할 수 있다. 하지만 해당 영역에 값을 쓴다 한들, 뭐 할 수 있는게 없었따..
그래서 대회당일에는 집중해서 보지 않았는데, 여기가 포인트인것 같다;; 현재 rsp는 0x7ff..d9d0을 가리키고 있고, snprintf함수가 호출되면서 돌아갈 주소를 스택에 push한다.
그렇다면 fsb를 이용하여 저기 주소 하위 한바이트를 0x14로 변경하면 된다. 하지만 0x7ff..d9c8을 가리키고 있는 주소는 현재 스택에 없다. 없으면 만들어 주면된다.
이렇게 %12$hhn 위치에 그냥 아무값을 쓴다. 0x68을 넣었다고 했을때, 현 rsp-8 주소와 $12$n 주소가 동일할때 까지 브포를 떄리면 된다. 2바이트 브포이긴 하지만, 테스트했을때 꾀 높은 확률로 들어간다.
snprintf 안으로 들어가면 실제 vsnprintf에서 fsb가 발생하고, 해당 함수가 호출되면, 저 빨간줄친 돌아갈 주소가
요렇게 바껴서 ret가 될때 win함수로 돌아간다.
3. 풀이
내가 짠 익스코드가 아니기 때문에 코드는 따로 업로드 하지 않겠습...
4. 몰랐던 개념
몰랐던 개념은 없었지만, 그냥 아직 경험과 기술이 부족한듯 싶다.
많이 CTF도 나가봐야함을 느낌.