Disobey 2023 hacker badge challenge writeup

This is a barebones writeup of the Disobey 2023 hacker badge challenge and the journey to solve it

General chain of breadcrumbs

  1. Twitter post that kouvosto telecom has made some opsec mistakes
  2. Google queries, find Seppo Aapakka as a kt employee
  3. Find seppo’s FB posts and the saboten group aka saboten biomedical
  4. Find the MRI image
  5. Read the barcodes on the image / find the tweet from saboten biomedical
  6. Go to saboten.kouvostotele.com and find the correct firmware binary
  7. Realize that the correct stuff to reverse engineer is the last 64kb of the binary
  8. Reverse the x86 real mode stuff, get new url
  9. Fetch the invoice pdf from files.kouvostotele.com
  10. Notice the forensics.uxin.fi link and go there
  11. Notice the news about secure-files.uxin.fi
  12. Figure out a way to access that virtual host
  13. Find secure-files.uxin.fi/backup/users/timi/.git
  14. Retrieve the whole git repository
  15. Find the private gpg key from the repository
  16. Crack the password on the key
  17. Decrypt the secure uxin password file
  18. Use the http basic auth on secure-files.uxin.fi
  19. Use quic and http basic auth on secure-files.uxin.fi
  20. Get the iphone disk image
  21. Extract the iphone image with dar
  22. Dig around the image, find emails
  23. Get the packet capture from email attachments and the password from the email
  24. Analyze the packet capture, get the data submission domain name, DNS server, submission url and dns update key name and the key
  25. Do dynamic dns update on crunch.kt.3g.re domain and listen on port 80
  26. Get the “sensitive data”
  27. Base64 decode and hex decode and get the store link for the hacker badge

MRI picture

Aztec code

  • Did some manipulation on the image, mainly adjusted the gamma curve to get the code to scan.
  • Contents of the code are:
Saboten Biomedical
NX-H128 S/N 000000001
FCC ID: KRJFSICIIFJEIRKS
  • Nothing really here, the FCC id just seems like random keyboard smashing
    • EDIT: FCC ID is base32 for “TRY HARDER”

dotcode

  • Getting this to work was little challenging, no image manipulation seemed to work.
  • In the end I redrew the code by hand on another layer, and that worked
  • We get a URL, lets go ftp://saboten.kouvostotele.com/prod/sb-ihv/firmware/NX-H128/0-00-3.1/bin/bin.7z

Alternative route

After solving the challenge I was made aware that there was a alternative route to the next stage. That involved finding a tweet from the Saboten Biomedical user, which contains a link to the root of the ftp. Unless one has been able to scan at least one of the barcodes from the image this route requires finding the correct firmware image among all of the crap on the site.

ftp.saboten.kouvostotele.com

  • A story .odt file and bunch of compressed firmware images
  • Seems like that only the one pointed by the URL contains real data
  • The file is a ELF, with bunch of extra data on the end
  • Loading it into ghidra it is just freebsd “sleep” command
  • There are some strings refering to PCI and A padding in the end
  • Looking with binwalk entropy graph there is a entropy edge at almost the start of last 64kb of the file
  • There are some interesting strings refering to credentials xored with A on the part
  • file doesn’t recognize it
  • After a while it seems to be real mode x86 binary, the special 16bit(ish) execution mode of a x86 used for initialization on boot
    • Load it into ghidra and start digging in
    • Parts of the program seem to be
      • Initialize a heap allocator
      • Enumerate pci devices and save their config registers
        • Doesn’t look for PCI bridges and busses behind them, sloppy!
      • Execute a function that fails to decompile initially, due to polymorphic code
        • Use ghidras bytes window to patch the bytes, decompile and recreate the function
        • Does some xors and copys, add the xor region as ghidra overlay
        • xor the key mask and the checked input to get ftp://kt:bl4de_runn3r@files.kouvostotele.com

files.kouvostotele.com

Only one pdf here, a invoice for iphone forensics done by https://forensics.uxin.fi

forensics.uxin.fi

  • Browse the web and run ffuf on it
  • Nothing hidden found on the main domain
  • There is a news entry announcing secure-files.uxin.fi but that domain points to localhost

secure-files.uxin.fi

  • Accessed by passing the “Host: secure-files.uxin.fi” header to requests to forensics.uxin.fi.
  • Index is asking for http basic auth
  • Start ffufing to see if there is anything to be found: ffuf -w ~/wordlists/common.txt -H "Host: secure-files.uxin.fi" -u https://forensics.uxin.fi/FUZZ -fc 401
  • Basic ffuf finds backups/users dir.
  • Construct a wordlist from all the names from the main uxin website
  • We find backups/users/timi/.git
    • No directory listings enabled
    • Get .git/config and then .git/refs/heads/master
      • Start walking the objects with the aid of git cat-file
      • Get the whole bare repository in this manner
      • Later I found out that there would have been utilities like https://github.com/WangYihang/GitHacker
        • Didn’t test the utilities myself, there weren’t that many objects to fetc
    • Exploring the git timi made some mistakes with his gpg keys
      • first commit has .backup/public.key that is actually a private key
        • it has a passphrase
        • Try to crack it with john
          • Success with rockyou wordlist, passphrase is iloveyou!
      • there are some encrypted files in .password_store
        • uxin/secure/timi.gpg contains a high-entropy string
          • Works for the basic auth

Basic auth

curl -v -L -u "timi:{/w8P;;}aED,{8s$" -H "Host: secure-files.uxin.fi" https://forensics.uxin.fi/
  • Has a UDP joke image in the root
  • ffuf around and find another image
    • Another UDP meme…
  • Portscan the site for open udp ports, find nada
  • Do some googling around, like “http udp” to see if there are any udp http clients around
    • http/3 aka QUIC is UDP!
  • try with browser, no dice
    • wireshark packet capture reveals we need draft-27 - draft-29 support
  • curl has experimental support for QUIC, not enabled by default…
    • try and try and try to compile it from few different instructions, all using various git heads and written 1+ year ago…
  • Get the index, a message telling us to analyze a iphone dump and a 3Gb 7z archive
  • The docker curl gets only around 1Mb of data per invocation before the connection dies…
    • Just try harder and wrap the curl into a loop with -m 3 to limit execution to 3 seconds and -C - to continue previous transfer
      • while ! curl -m 3 -C - --http3 -v -L -u "timi:{/w8P;;}aED,{8s$" -H "Host: secure-files.uxin.fi" https://forensics.uxin.fi/KT_Forensics_iPhone_image.7z --output KT_Forensics_iPhone_image.7z; do echo retrying; done
        • Experiment with the timeout values to get the best performance out of your potato
      • After one afternoon we have data to analyze!

iphone dump

  • Heaps of data
  • A mysterious dar extension for the filesystem image
  • a nice article was found by google: https://abrignoni.blogspot.com/2020/03/so-you-have-dar-file.html by search “iphone dar forensics”
  • extract the dar file with dar, doh
  • Try APOLLO, get bunch of useless timeline stuff
  • Try KAPE, can’t get it to execute at all
  • Try iLeap, https://github.com/abrignoni/iLEAPP, not having high hopes
    • Actually works and gives nice html to explore
  • Only Whatsapp and mail have been really used on the phone
    • Whatsapp messages have memes and story stuff
    • Mail has few interesting messages
      • iLeap only shows partial contents and not the attachments, but shows a GUID
      • find that GUID on the extracted data with find
      • private/var/mobile/Library/Mail/CEAB6F3D-B8DC-47DD-863F-F21B818B0064/INBOX.imapmbox is where the good stuff lies
      • More story stuff and also lb.zip and the password for it in the mail, hunteR2
  • Additional digging around reveals that the checkra1n jailbreak/rootkit has been installed on the phone, in private/var/checkra1n.dmg
    • Probably used for creating the image

lb.zip

  • The packet capture leads to http://94.237.108.204/submit/ submissions are made with POST requests
  • Interesting ips:
    • 94.237.108.204 - submission server and DNS server for kt.3g.re
      • 53/tcp open domain (unknown banner: Kouvosto Telecom DNS server v0.01 beta - in scope)
    • 94.237.109.63 - some host, SSH and 8089 open
  • domain crunch.kt.3g.re is used for the data submission, has TTL of 2
  • a dynamic update on that domain is done on the packet capture
    • dynamic dns key name: updatekey. from packet capture
      • the dot is important for tools, needs to be fully qualified…
    • if we could update it we could get a connection of the sensitive data…
  • Some tries with CVE-2017-3143 exploits, didn’t lead to anywhere…
    • Things just didn’t work at all due to me not having the dot expected by the libraries in the key name…
    • Was using the wrong hmac function at the start, needed to have sha512, had sha256
    • But in the end the exploit didn’t seem to work, not sure if I had wrong payload sizes due to different hmac
  • Took another look into the packet capture from a nudge of my sparring partner
    • There is a kouvosto keyserver transaction hidden in the TCP control packets
      • Wireshark → right click on packet → view flow → tcp FTW
    • We have the update key
  • Modify and hack the exploit PoC, get a working dns update
  • do the dns update && sudo nc -l 80
  • get the sensitive data from the field

Sensitive data

POST / HTTP/1.1
Host: crunch.kt.3g.re
User-Agent: Kouvosto Telecom sensitive data submission agent (military grade)
Transfer-Encoding: chunked
Content-Type: application/ktson
Accept-Encoding: gzip

379
{
  "beacon_name": "chipper",
  "beacon_ip_address": "172.16.104.32",
  "beacon_model": "KVR-L200",
  "beacon_firmware": "0.7.1b.83340",
  "beacon_serial": "80860600021",
  "beacon_location": "Saboten Biomaterial Factory #7",
  "timestamp": 2718658601,
  "meta_data":
    {
      "datatype": "KTBBD",
      "version": "0.7a"
    },
  "events": [
    {
      "rssi": "-42",
      "data": "aHR0cHM6Ly9ob2x2aS5jb20vc2hvcC9EaXNvYmV5L3Byb2R1Y3QvNTgyODkxZDU3YjcwZDEyOWYwN2NkZjJjNzNlMzg4NTMv",
      "srData": "I29pc2pvaGFja2VyYmFkZ2U=",
      "timestamp": 2718658599,
      "device_ktid": "5468616e-6b20-796f-7520-666f72207472-79696e672068-617264657221"
    },
    {
      "rssi": "-36",
      "data": "SXQncyBkYW5nZXJvdXMgdG8gZ28gYWxvbmUhIEhlcmUsIHRha2UgdGhpczo=",
      "timestamp": 2718658594,
      "device_ktid": "5468616e-6b20-796f-7520-666f72207472-79696e672068-617264657221"
    }
  ]
}

Feedback

  • The start requiring selling ones soul to Zuckenberg sucks
  • The QUIC part was made hard and annoying just by the lack of good easily available tools
  • Finding the real binary and the correct offset in the firmware blob was annoying and required quesswork
  • Little bummed out that the TSIG bypass exploit didn’t work, would have been nice different path on that part

For the binary reverse engineering the archived ghidra project is here: https://kissa.depili.fi/disobey_2023.gar

To open start ghidra, close any currently open project and then select file → restore project

  • The start requiring selling ones soul to Zuckenberg sucks
  • Finding the real binary and the correct offset in the firmware blob was annoying and required quesswork

for an alternative approach to the start of the puzzle which bypasses both of these, check out my quick and dirty writeup :wink:

I got myself to step 6 and eventually stumbled through the ELF file reversing.

How did you figure out that you should XOR the latter half of the binary?
And after that how did you know that it was x86 real mode binary?

Following this post I tried lobbing off the 65536 bytes (i.e. at what point binwalk calls: “Falling entropy edge”) and XOR-ing that and I was successful at getting the “interesting strings” out, but Ghidra wouldn’t recognize the file and when I forced the x86 real mode it found no functions.

The real mode code isn’t xorred, mostly. Just a part of it that is xorred by the code and then executed. Lob off the last 64kbo of the file and load it straight into ghidra.

Finding the real mode stuff involved trial and error, but If you look at the shinmai’s writeup there are some hints for that I missed.