1.1 challenge
1.2 file & checksec
모든 보호기법이 걸려있다.
1.3 Source code
IDA로 디컴파일해서 나온 소스코드이다.
1.3.1 코드 설명
size[1] 같은 경우는 canary 값 같고
sub_B4E는 init 같은 함수, 즉 버퍼를 비우는 함수같다.
그리고 v3에 malloc으로 할당을 받기 시작하고
v3 안에 1을 넣는다.
그리고 v3의 주소를 %p로 노출시킨다.
size[0]에 0을 넣고, 그 후 scanf로 8바이트 만큼의 값을 받는다.
그리고 v4는 입력받은 size 만큼 malloc으로 할당 받기 시작한다.
read로 v4에 입력받은 size 만큼 값을 입력받고
v5에 size를 넣고
v4[size[0]-1]에 0을 넣은 후
v4를 출력해준다.
그 후 if문에서 v3의 값이 0인 경우 system("cat /flag")를 실행시켜
flag를 추출해준다.
1.3.2 문제 추측
여기 까지 놓고 보았을 때,
scanf("%lu", size) 에서 size 값을 많이 받은 후에
v4[size[0]-1] 부분에서 BOF를 일으켜서 v3를 0으로 변조시키는
(이 경우 cat /flag가 실행됨)
즉, BOF 문제 같다.
2. Analysis
pwndbg로 main까지 가보자
해당 실행 파일은 stripped 되어 있기 때문에, 심볼이 없어
main 부분까지 직접 동적으로 찾아야한다.
main
열심히 디버깅을 해서 main까지 도착한 모습이다.
main에서 하나씩 실행해 보자
1. v3 주소 leak
malloc으로 0x4000만큼, 즉 위에서 코드 설명할 때,
v3 = malloc(0x4000)을 나타낸 부분이다.
mov qword ptr [rax],1 부분이,
*v3 = 1 부분이다.
그 후 printf("Leak: %p", v3) 부분이 나온다.
즉 v3의 주소가 유출이 된다.
2. size 입력
8바이트 만큼의 size를 받는 부분이다.
아무 값이나 넣어보자
값을 넣은 결과이다
계속 실행해보자
3. read 부분
계속 실행 해보니,
위에서 받은 size 값 만큼, malloc을 통해서 v4(buf)가 heap 영역에 메모리 할당을 받고
read 입력까지 받는 모습이다.
3. Vulnerability
일단 우리가 알고 있는 정보는 v3의 주소를 알고 있다.
여기까지 보았을 때,
"취약점이 어디있지?,
BOF로 v4[size-1] = 0에서 size값을 많이 줘서
BOF를 일으켜 v3값을 덮어씌운 후,
if(*v3) { system('cat /flag'); } 를 실행시켜야하는 것까진 알겠는데,
어떻게 v3 값을 변경시키지?
3.1 malloc 반환값
역시나 구글링이 답이다.
malloc은 적당한 size 만큼 할당을 받게 되면,
heap 영역의 메모리 주소를 "반환" 해준다.
즉, 적당한 size만큼 할당을 하게 되면, "반환값"을 "heap" 영역의 주소로 할당해준다.
하지만, 적당한 size가 아닌, 엄청나게 큰 size의 값을 할당받게 된다면 어떻게 될까?
이런 경우, malloc 입장에서는 "어? 이거 다른 영역까지 침범할 수 있겠는데?"
하면서, 반환값을 NULL로 해준다.
즉, v4 = malloc(size)를 했을 때, v4값이 NULL이 되는 것이다.
3.2 *v3 = 0 만들기
현재 *v3 = 1 이다.
그리고 v3의 주소는 이미 노출이 되어 알고 있다.
scanf("%lu", size); v4 = malloc(size[0]);
이 부분에서 size 값을 엄청나게 많이 할당을 하게 된다면
v4 주소가 0이 된다.
v4 주소가 0이 된다면, v3의 주소(0x7f......)정도이니 굉장히 큰값이므로
v4[size[0]-1] = 0;
이 부분에서 v3[0] = 0으로 만들 수 있다.
3.3 확인
read(0, v4, size[0]) 부분에서
위의 scnaf에서 size를 v3의 주소만큼 줬더니
v4 = malloc(size[0])에서 malloc 반환값이 0이 되었음을 알 수 있다.
위에 보면 buf가 0이다.
4. Exploit
이제 *v3값이 0으로 만들 수 있게 되었다.
exploit 코드를 짜보자
from pwn import *
p = process('./welcome')
p.recvuntil('Leak: ')
leak = int(p.recv(14), 16)
p.sendlineafter('Length of your message: ', str(leak+1))
p.sendlineafter('Enter your message: ', str(123))
p.interactive()
5. Result
flag 값은 본인이 임의로 작성한 것이다.
성공적으로 cat /flag를 통해
flag를 추출해 냈다.
6. 알게 된점
malloc의 반환값이 매우 크면 NULL을 반환한다는 사실을 깨달았다.