I was very happy to be back at OWASP Toronto on Jan 23, 2019, presenting on "Back to the Future of AppSec - Developing Secure Smart Contracts" Please find the presentation link below. Hope everyone enjoyed the talk, if there's any questions please feel free to reach out anytime! Cheers, -Jamie
On Nov, 8,9 2018 I had the opportunity to help our team SomeRandomName run the Ottawa B-Sides CTF. This was our 5th year running the event with over 120 participants registered. Our biggest event yet! It’s come a long way from 30+ people huddled into a classroom bridged to the internet over a D-Link DIR-825 - affectionately known as the “D-Link of Power”. (RIP) The challenge track I wrote consisted of a series of three reversing challenges based on Web Assembly. Other than wanting to get a chance to play more with Web Assembly myself, Web assembly was something that many teams would be unfamiliar with and much of the standard reverse engineering tooling wouldn’t be as helpful. Hopefully leveling the playing field a bit between teams with and without commercial tools. All of the track could be solved with Chrome Developer Tools (seen below for the thanks example) or tools within the Emscripten SDK. I know Radare2 has support for WASM as well. Essentially the challenges were crackmes that were easy to easy-moderate in difficulty, but layered with web assembly to make it spicy. RESULTSAt the end of a hard fought 48 hours the scoreboard was as follows: Congrats to the winners! Building the WallThe challenges were constructed with the Emscripten SDK on a Ubuntu 16.04.5 LTS box. The SDK installed seamlessly using the instructions on the Web Assembly getting started: $ git clone https://github.com/juj/emsdk.git $ cd emsdk $ ./emsdk install latest $ ./emsdk activate latest Building a sample web assembly program was a straight forward process. emcc hello.c -s WASM=1 -o hello.html The compilation process produced a larger javascript file and html in addition to the hello.wasm file. At the time I didn’t really pay much attention to them. Instead I simply tried to load my hello.wasm file directly only into a sample page copied from http://webassembly.studio. Unfortunately, it produced a number of cryptic errors, relating to an invalid environment, after a bit of googling I found that the large javascript file produced by the compiler wasn’t just for show (unsurprisingly) but it prepared the execution environment (memory size, stack size, error handler, etc) that was needed to properly execute the web assembly binary. Fortunately, I was able to find a much smaller version, which provided the minimum set of items required to execute the web assembly file. (thank you Alexanderby - https://github.com/WebAssembly/binaryen/issues/670#issuecomment-344546116) Taken from the first challenge the required basic environment setup is shown below const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 }); const importObj = { env: { abortStackOverflow: () => { throw new Error('overflow'); }, table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc' }), tableBase: 0, memory: memory, memoryBase: 1024, STACKTOP: 0, STACK_MAX: memory.buffer.byteLength, } }; The next question was the how to call a specific web assembly function from the web page. This took way longer than it should have for me to figure out, but eventually I got a minimized set as below. C Code Int is_this_the_flag() { // crack me algo here. } JS var result = instance.exports._is_this_the_flag('flag'); Compilation emcc wall.c -s ONLY_MY_CODE=1 -s WASM=1 -s EXPORTED_FUNCTIONS=”[‘ is_this_the_flag()’]” -o wall.html (And yes there’s no _ in the C file function name, but it’s needed when declared as a export function.) The Wall #1(Solved by 6 Teams) This challenge was a straight forward character by character if statement. The challenge was meant to be more of a tooling exercise and to get everyone familiar with web assembly - due to the way the file is compiled none of the actual flag characters appear in the binary. (nice!) The main check for the flag was: int is_this_the_flag(int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8, int c9, int c10, int c11, int c12, int c13, int c14, int c15, int c16, int c17, int c18, int c19, int c20, int c21, int c22, int c23, int c24, int c25, int c26, int c27, int c28, int c29, int c30, int c31, int c32, int c33, int c34, int c35, int c36, int c37, int c38, int c39, int c40) { if(c1 == 'f' && c2 == 'l' && c3 == 'a' && c4 == 'g' && c5 == '{' && c6 == 'g' && c7 == '0' && c8 == '0' && c9 == 'd' && c10 == 'W' && c11 == '0' && c12 == 'r' && c13 == 'k' && c14 == 'B' && c15 == 'u' && c16 == 't' && c17 == 'W' && c18 == 'e' && c19 =='A' && c20 == 'r' && c21 == '3' && c22 == 'J' && c23 == 'u' && c24 == 's' && c25 == 't' && c26 == 'G' && c27 == '3' && c28 == 't' && c29 == 't' && c30 == '1' && c31 == 'n' && c32 == 'g' && c33 == 'S' && c34 == 't' && c35 == '4' && c36 == 'r' && c37 == 't' && c38 == 'e' && c39 == 'd' && c40 == '}') return 1; return -99999; } As you can see it’s just a REALLY long if statement. Using an array like a normal person was a little too clean in the disassembly. (And typing out the above was much more fun..) The flag was flag{g00dW0rkButWeAr3JustG3tt1ngSt4rted} The Wall #2Solved by 3 teams) This challenge incorporated a more complicated method of verifying the flag. The CRC32b value of four characters at a time were calculated and compared against the desired value for the flag. The challenge was to recognize the algorithm, extract the desired values and calculate the flag four bytes at a time. The core of it was a CRC32b algorithm (from: https://forum.arduino.cc/index.php?topic=342371.0) unsigned int crc32b(unsigned char *message) { int i, j; unsigned int byte, crc, mask; i = 0; crc = 0xFFFFFFFF; while (message[i] != 0) { byte = message[i]; // Get next byte. crc = crc ^ byte; for (j = 7; j >= 0; j--) { // Do eight times. mask = -(crc & 1); crc = (crc >> 1) ^ (0xEDB88320 & mask); } i = i + 1; } return ~crc; } The flag was passed in by 4 bytes at a time to generate a crc32 value. int is_this_the_flag(int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8, int c9, int c10, int c11, int c12, int c13, int c14, int c15, int c16, int c17, int c18, int c19, int c20, int c21, int c22, int c23, int c24, int c25, int c26, int c27, int c28, int c29, int c30, int c31, int c32, int c33, int c34, int c35, int c36, int c37, int c38, int c39, int c40) { unsigned char msg1[5]; msg1[0] = c1 & 0xFF; msg1[1] = c2 & 0xFF; msg1[2] = c3 & 0xFF; msg1[3] = c4 & 0xFF; msg1[4] = '\0'; unsigned int frag1 = crc32b(msg1); unsigned char msg2[5]; msg2[0] = c5 & 0xFF; msg2[1] = c6 & 0xFF; msg2[2] = c7 & 0xFF; msg2[3] = c8 & 0xFF; msg2[4] = '\0'; unsigned int frag2 = crc32b(msg2); unsigned char msg3[5]; msg3[0] = c9 & 0xFF; msg3[1] = c10 & 0xFF; msg3[2] = c11 & 0xFF; msg3[3] = c12 & 0xFF; msg3[4] = '\0';; unsigned int frag3 = crc32b(msg3); unsigned char msg4[5]; msg4[0] = c13 & 0xFF; msg4[1] = c14 & 0xFF; msg4[2] = c15 & 0xFF; msg4[3] = c16 & 0xFF; msg4[4] = '\0'; unsigned int frag4 = crc32b(msg4); unsigned char msg5[5]; msg5[0] = c17 & 0xFF; msg5[1] = c18 & 0xFF; msg5[2] = c19 & 0xFF; msg5[3] = c20 & 0xFF; msg5[4] = '\0'; unsigned int frag5 = crc32b(msg5); unsigned char msg6[5]; msg6[0] = c21 & 0xFF; msg6[1] = c22 & 0xFF; msg6[2] = c23 & 0xFF; msg6[3] = c24 & 0xFF; msg6[4] = '\0'; unsigned int frag6 = crc32b(msg6); unsigned char msg7[5]; msg7[0] = c25 & 0xFF; msg7[1] = c26 & 0xFF; msg7[2] = c27 & 0xFF; msg7[3] = c28 & 0xFF; msg7[4] = '\0'; unsigned int frag7 = crc32b(msg7); if(frag1 == 0xD1F4EB9A && frag2 == 0xA8077225 && frag3 == 0x526BC817 && frag4 == 0x3FCE667C && frag5 == 0x3F2B257C && frag6 == 0x698FA851 && frag7 == 0x587EBCE6) return 1;} Solving for each of the fragments produced:. flag{WellD0n3AllM0stTh3r3} The Wall #3(Solved by 1 Team – Congrats WeDontHaveOne!)
Where as the Wall #2 was more challenging from a algorithmic point of view, Wall 3 focused on obfuscation targeted at the Chrome Developer Tools, incorporating 40 or so separate function calls to check the encrypted key value, as there was a little bit of crypto mixed. (multiple byte XOR with the key CYBER). Using the chrome developer tools this would spawn into 40 or so separate instanaces. From the C file. For Example: int check39(int c) { if( c== 0x38) return 1; return 0; } int check38(int c) { if(c == 0x2c) return 1; return 0; } int check37(int c) { if(c == 0x68) return 1; return 0; } With the following check int is_this_the_xord_flag(int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8, int c9, int c10, int c11, int c12, int c13, int c14, int c15, int c16, int c17, int c18, int c19, int c20, int c21, int c22, int c23, int c24, int c25, int c26, int c27, int c28, int c29, int c30, int c31, int c32, int c33, int c34, int c35, int c36, int c37, int c38, int c39, int c40) { if(check1(c1) && check2(c2) && check3(c3) Here was the main javascript (async () => { const fetchPromise = fetch('./wall3.wall'); const { instance } = await WebAssembly.instantiateStreaming(fetchPromise, importObj); var result = instance.exports._is_this_the_xord_flag('flag'); if (result == 1) { result = "CONGRATS - THATS THE XORD FLAG - USE THE SECRET CYBER KEY FOR THE REAL FLAG... GOOD LUCK"; } else { result = "NOTHING DEFEATS THE POWER OF THE BIG DATA AI MACHINE LEARNING BLOCKCHAIN! "; } document.querySelector('main').textContent = ` ${ result }.`; })(); The sequence that would have returned a true value was //25-35-23-22-29-00-36-0c-22-20-77-2d-18-1c-3d-16-14-23-21-61-02-2a-31-00-3f-21-35-1b-02-20-26-6d-36-71-35-22-68-2c-38 Which if XOR'd with the key CYBER produced the flag. Wall Materials Here's the challenges if you'd like to climb the wall yourself, have fun! the_walls.zip (217k) I had the opportunity to present at OWASP Toronto on May 25, 2017, discussing Web Based CTF problems, providing a basic methodology & approach, some tips and tricks, walking through a few past challenges and concluding with 4 challenges for the attendees to solve provided on a VM, Each highlighting very real application security issues.
Hope everyone enjoyed the talk, if there's any questions feel free to reach out anytime. Cheers, -Jamie
This weekend I presented at the Toronto CyberSecurity meetup on how to get started with Capture The Flags and other areas of security gamification. Please find the presentation below. Cheers, -Jamie
Last Month our CTF team “SomeRandomName” hosted the CTF at BSides Ottawa for over a hundred competitors. It was our third year running the event and largest one yet, both in terms of number of attendees and challenges. Scoreboard The CTF had used the mellivora scoreboard the last few years and while incredibly reliable and easy to use, we were looking to provide something new to the players this year. Eventually we settled on the Facebook CTF scoreboard. The Stats Number of Challenges: 43 Number of Correct Flags submitted: ~2600 Number of Incorrect Flags: ~28000 ( ahhh trivia) Challenges solved by at-least one team: 40 (3 challenges were not solved) Challenges not released: 2 The first part of this post provides a look into the inner workings of the CTF from our perspective. The second are write-ups for a number of the challenges. Behind the Curtain Network We moved to a more segregated topology this year, with a player network, network track, infrastructure and management network. Our long-term plan is to move to team containers which would contain separate instances of the game. A high-level diagram can be found below: Challenges We aim for a CTF of average difficulty with a wide variety of intro to mid-level difficulty problems. The goal is to have something for everyone, with plenty for new players to work on - especially the CTF101 / Trivia categories, where someone can jump in and work away between talks - but with a good number of harder problems to challenge the more experienced CTFers.. Our categories included Network, Forensics, Web, CTF101, RE and Crypto. We also had several “guest” challenges contributed. These were all excellent and rounded out the higher end of the difficulty spectrum. Red Alert! Shields up! After the doors opened, the 100 plus players began to connect and to explore the game, initially everything seemed fine…. It soon became apparent that there were issues with the game network. Challenges would appear to be randomly unavailable, and tools like nmap would simply die half way through a scan. Once a session was established it seemed to be fine but that initial connection would randomly never complete. This was puzzling as the network latency looked normal. Meanwhile it became apparent that the scoreboard wasn’t holding up either, gradually becoming completely unresponsive. ZeroCool never had to put up with this. Looking at the scoreboard box we saw that async-mysql process (a hhvm plugin) was pinning a full CPU and a there were thousands of connections in the timewait state. We gave the box additional resources and rebooted. This alleviated the problems for a short time, but the scoreboard eventually began to slowly grind to a halt again. The puzzling thing was that after assigning more resources, the scoreboard CPU, was only at 10-20%, memory wasn’t in swap and the network traffic was minimal as well (with little to no errors). Chasing the excess timewaits, we adjusted the mysql configuration to close timewaits within seconds rather than minutes. This appeared to help a little but nothing like rebooting the box, which would give a solid 15-30 minutes of good performance before it slowed to a crawl again. In the end we isolated the problem to hhvm (the facebook developed JIT php environment) and its async-mysql plugin, we noticed a number of issues had been raised in the Facebook CTF git to improve performance in this area. We started going down the code rabbit hole to determine the full root cause, but with a room full of increasingly restless CTFers there wasn’t time to debug in depth. We eventually settled on a highly effective but rather inelegant solution: A cron job which restarted the hhvm service every 15 minutes. #Solved. I get static route, you get a static route, everybody gets a static route! Going back to the problem of the challenges randomly not responding, we found asymmetric routing between the players and some of the challenges. Packets from the infrastructure network (where the challenges were) would first go to the venue gateway before bouncing back to the player network, which caused problems with packets traversing the state tables on the routers. Of course during our tests with only a handful of users the night before this problem did not manifest itself. Again, some front line maintenance was used to correct the issue. A default static route to the player network was pushed to each of the challenge boxes. route add -net 10.20.128.0/21 gw 10.20.1.11 dev <ent> Both issues are great cases of problems that only become apparent when the system is under load. Alls Well that Ends Well By about 1300, we had the problems with the routing, wireless and scoreboard under control and by 1400 the experience was back to what we wanted to see. To give teams a bit more time, we extended the game by an hour the first day. Bsides Ottawa Challenges Write ups The following are brief writeups for some of the challenges. Ctfinder (category: CTF101/Web) After building the most awesome CTF team, one had was presented with Nedry from Jurassic Park informing you, that did not say the magic word. https://www.youtube.com/watch?v=RfiQYRn7fBg Using burp, you can add magicword to the parameters in the post request to which the application would responder "get closer" . Taking that one step farther adding magicword=please will get the flag. Internet of Poop (category: CTF101/RE) An internet connected toilet. Ahh it’s nice to live in the future. There was a small misdirection to look like a sqli challenge but it was a infact a basic firmware reversing challenge. The “admin credentials” to the toilet were in the emergency firmware reset image. Simply binwalking the image and extracting the zipfile within, yielded the creds in base64 format. Security by Obscurity – Big Data Edition (category: CTF101/Network) The version is a little old so we moved it to a port no one could possibly find it. Since we passed our compliance test, we know we’re secure. Groovy… A vulnerable version (CVE-2015-1427) of elasticsearch (1.4.2) was running on the host, the only "hardening" was the REST API port remapped to a high port. After locating the service, here’s the metasploit module to use. https://www.rapid7.com/db/modules/exploit/multi/elasticsearch/search_groovy_script Then read the flag in \flag.txt Off By One (category: CTF101/Crypto) (attached) I think someone broke a bit. In taking a closer look at the key, the key had one bit flipped. The solution was to write a small script which iterated through the key flipping each bit. This quickly yield the solution. Flag: B1tFl1pp1nAllDayEv3yD4y Mo Data Mo Problems (category: CTF101/Network) If you haven big data problems I’m sure it’s no fun I got 99 problems but authentication ain’t one This problem featured an elasticsearch node, with the REST API remapped and behind nginx on port 8080. Ironically the real effort behind this challenge was not the initial creation but armoring it so players couldn’t delete or modify the flag. Here’s the solution: curl -X GET 'http://10.20.16.31:8080/_search?q=*:*' Result {"took":2,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":1.0,"hits":[{"_index":"flagstorage","_type":"flag","_id":"1","_score":1.0,"_source":{"user" : "flagowner", "flag" : "COnnECt_ME_To_THe_InterNet_WhAt_CoUlD_Go_Wr0ng"}}]}} The flag: COnnECt_ME_To_THe_InterNet_WhAt_CoUlD_Go_Wr0ng Remote Desktop compromise by weak creds (Category: Old School Trivia) The augments should have changed the code from this value If you search augments in google depending your search history it may go to “Enterprise” augments references, which is definitely not old skool enough. “Augments” refers of course to our favorite augment “Kahn” from Star Trek 2 and TOS:Space Speed, Once you find the video the flag you’re looking for is the prefix code which allows one to remote into a ships console. The flag can be found in this video clip. Enjoy!: https://www.youtube.com/watch?v=WCpYqWAIwFA And if that’s not enough, here’s a reddit thread debating the merits of 5 digit code to control star ships... https://www.reddit.com/r/DaystromInstitute/comments/2yl22y/are_prefix_codes_a_fatal_security_flaw_of/ What’s inside (Category: Old School Trivia) I thought the title of the challenge would give it away. But apparently, some teams spent hours on this problem. I know how it is on CTFs, sometimes it’s very hard to put a problem down. The challenge featured an ascii art of the HEMAN logo. The title was a clue or it could be compared to the original. It was ASCII Stego. A quick search of google for “Ascii art stego” reveals the online decoder here: http://pictureworthsthousandwords.appspot.com/ The Flag IS: YoU_Will_NEvEr_FinD_TH1S Off the Rails We released this challenge with about 2 hours or so to go. It consisted of a vulnerable ruby on rails install running a web console. The web console of course allowed you to run ruby code. The easiest path to a shell, which no one did, would have been to run the metasploit module for this particular version of ruby on rails webconsole. Alternatively using the command injections you could have seen that you were running as the user cyber (the title of the app was cybercybercyber as well.). At that point if you had tried to ssh to the box with the account/password cyber/cyber you would have a shell. Instead many many many people tried to get a reverse shell with netcat, over and over and over again. Unfortunately the version of netcat on the box did not have the -e option. #sorrynotsorry. - Check out https://pen-testing.sans.org/blog/2013/05/06/netcat-without-e-no-problem Unfortunately the flag was readable only by root. Fortunately there were a number of priv escs on the box, the simplest was sudo as cyber was in the sudo group. |
SRNSECThe "unvarnished" opinions of SRNSEC. Archives
February 2019
Categories |