RSS

CSAW CTF 2013 – Miscellaneous 200

0 Comments | This entry was posted on Sep 29 2013

For this challenge the first thing I did was as most would do, open the image in an image viewer program (specifically the Windows default image viewer, nothing fancy.) I didn’t see anything eye-catching, and when viewing the file in a hex editor I didn’t see anything immediately out of the ordinary either, there was nothing packed in with the PNG, it looked as a normal PNG would look, with lots of arbitrary junk data, and apparently indicating the image was taken from an IPhone 5. I didn’t realise how important this would be for me to notice at that point in time, but it definitely helped.

Next, I used a program that utilizes ‘libpng’, a very reputable and extensible PNG reference library that has existed and been tested for some years now, to analyze the more technical data for the PNG.

I got a notification that read something about the CRC in the IHDR chunk (the first few bytes of the PNG): “Incorrect crc for IHDR chunk (is c1d0b3e4, should be fcc410a8)”

I then tried changing this integer value with a hex editor at offset 0x1D to the following 4 bytes {0xFC 0xC4 0×10 0xA8}, and in doing so actually gave me more errors, including CRC errors for IDAT chunks, and there were a lot of them. Coincidentally, I decided to make sure that I was heading in the right direction and making sure that the CRC expected was actually the original, and perhaps it was changed deliberately for this challenge. So I checked the data that was currently there in the image header:

- 8 bits/sample (normal)
- truecolor+alpha
- non-interlaced

The one other thing that didn’t seem right, judging by the fact that the tEXt chunk points to an IPhone 5 running IOS 6.1.4, was that the image size was 3264×1681. This does not match any of the standard aspect ratios: 16:10, 16:9, 4:3 etc… And I also figured that editing this image in most image editors to crop it down to a portion of that full image would have possibly removed some of the remains of what the IPhone embedded within that image as a ‘mark’, upon resaving. (Everybody loves to advertise their copyright in there, you know how it goes.)

Through more research an IPhone 5′s camera roll aspect ratio is 4:3. The calculation gave me the idea that the image had somehow been chopped down height-wise.

3264*3/4 = 2448
2448 != 1681

Now, obviously there is no way to get that pixel data back once the image is chopped data-wise, so I tried changing the image size in the header from 3264×1681 to 3264×2448, and by opening the image again in some image viewer, voila! The key was written on the whiteboard the entire time. :)

Revisiting that hint they gave for this challenge part-way through my investigation also backed up this theory, but it wasn’t until I edited the header to match a 4:3 aspect ratio that it was apparent for what that down arrow really meant. All I knew was that this had nothing to do with width or anything horizontal.

There were exactly 2 bytes that needed changing in order to solve this challenge in the end, starting at offset 0×16, that indicate the height of the image. The values, originally, {0×06 0×91}, that needed to be changed to any sufficiently large value big enough to view that ‘hidden’ key on the whiteboard. I just matched it up to a 4:3 aspect ratio and changed those bytes to {0×09 0×90}.

Hack.LU CTF 2012: Big Zombie Business writteup

0 Comments | This entry was posted on Oct 25 2012

It’s a disaster! Not only that these useless piles of rotten meat obfuscate all their stupid code, they have also lost our precious root password, or “Flag” as they call it. Is there a chance you can reverse this obfuscation to extract the Flag?

credits: 200 +3 (1st), +2 (2nd), +1 (3rd)

So, we started to access the page in browsers. In chrome I couldn’t access it, so I used firefox instead for this challenge.

Now let’s see the source:


html,body{background:#000;height:100%;min−height:100%}#zombie{border:1px solid #fff;}#zombie2{color:#fff}
(\u0066\u0075\u006e\u0063\u0074\u0069\u006f\u006e(){\u0069\u0066(\u0074\u0079\u0070\u0065\u006f\u0066 \u0063\u006f\u006e\u0073\u006f\u006c\u0065==\u0075\u006e\u0064\u0065\u0066\u0069\u006e\u0065\u0064)\u0063\u006f\u006e\u0073\u006f\u006c\u0065={'\154\157\147':\u0066\u0075\u006e\u0063\u0074\u0069\u006f\u006e(){}};\u0073\u0065\u0074\u0049\u006e\u0074\u0065\u0072\u0076\u0061\u006c((\u0066\u0075\u006e\u0063\u0074\u0069\u006f\u006e(){\u0076\u0061\u0072 \u0063=\u0063\u006f\u006e\u0073\u006f\u006c\u0065.\u006c\u006f\u0067;\u0076\u0061\u0072 \u0075=\u0066\u0075\u006e\u0063\u0074\u0069\u006f\u006e(){\u0063('45\143\102\162\141\141\141\141\101\101\101\111\116\132\132\132\132','\143\157\154\157\16272\162\145\14473\146\157\156\16455\163\151\172\14572626060\160\17073')};\u0075(),\u0075(),\u0075();\u0072\u0065\u0074\u0075\u0072\u006e \u0075})(),100);\u0066=\u0066\u0075\u006e\u0063\u0074\u0069\u006f\u006e(){\u006c\u006f\u0063\u0061\u0074\u0069\u006f\u006e='\156\157\146\154\141\147'};\u006e={\u0076\u0061\u006c\u0075\u0065:\u0066,\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0061\u0062\u006c\u0065:\u0066\u0061\u006c\u0073\u0065};(\u0061=\u0064\u006f\u0063\u0075\u006d\u0065\u006e\u0074.\u0061\u0064\u0064\u0045\u0076\u0065\u006e\u0074\u004c\u0069\u0073\u0074\u0065\u006e\u0065\u0072)('\104\117\115\101\164\164\162\115\157\144\151\146\151\145\144',\u0066,\u0066\u0061\u006c\u0073\u0065);a('\104\117\115\116\157\144\145\111\156\163\145\162\164\145\144',\u0066,\u0066\u0061\u006c\u0073\u0065);\u0061('\104\117\115\103\150\141\162\141\143\164\145\162\104\141\164\141\115\157\144\151\146\151\145\144',\u0066,\u0066\u0061\u006c\u0073\u0065);\u0066\u006f\u0072(\u0069 \u0069\u006e \u006d=['\167\162\151\164\145','\167\162\151\164\145\154\156','\143\162\145\141\164\145\105\154\145\155\145\156\164','\141\160\160\145\156\144\103\150\151\154\144','\143\154\157\156\145\116\157\144\145','\151\156\163\145\162\164\102\145\146\157\162\145','\162\145\160\154\141\143\145\103\150\151\154\144','\143\162\145\141\164\145\105\154\145\155\145\156\164\116\123'])\u004f\u0062\u006a\u0065\u0063\u0074.\u0064\u0065\u0066\u0069\u006e\u0065\u0050\u0072\u006f\u0070\u0065\u0072\u0074\u0079(\u0064\u006f\u0063\u0075\u006d\u0065\u006e\u0074.\u0063\u006f\u006e\u0073\u0074\u0072\u0075\u0063\u0074\u006f\u0072.\u0070\u0072\u006f\u0074\u006f\u0074\u0079\u0070\u0065,\u006d[\u0069],\u006e);\u0076\u0061\u0072 \u0079=\u0061\u006c\u0065\u0072\u0074;\u004f\u0062\u006a\u0065\u0063\u0074.\u0064\u0065\u0066\u0069\u006e\u0065\u0050\u0072\u006f\u0070\u0065\u0072\u0074y(\u0077\u0069\u006e\u0064\u006f\u0077,'\141\154\145\162\164',{\u0076\u0061\u006c\u0075\u0065:\u0066\u0075\u006e\u0063\u0074\u0069\u006f\u006e(){\u0079('\142\162\141\141\141\151\156\172\172\172')},\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0061\u0062\u006c\u0065:\u0066\u0061\u006c\u0073\u0065});\u0076\u0061\u0072 \u007a=\u0070\u0072\u006f\u006d\u0070\u0074;\u004f\u0062\u006a\u0065\u0063\u0074.\u0064\u0065\u0066\u0069\u006e\u0065\u0050\u0072\u006f\u0070\u0065\u0072\u0074\u0079(\u0077\u0069\u006e\u0064\u006f\u0077,'\160\162\157\155\160\164',{\u0076\u0061\u006c\u0075\u0065:\u0066\u0075\u006e\u0063\u0074\u0069\u006f\u006e(\u0071){brain(z('\127\150\141\16447\16340\155\17140\172\157\155\142\151\14540\156\141\155\14577'),\u0071)},\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0061\u0062\u006c\u0065:\u0066\u0061\u006c\u0073\u0065});\u0066\u006f\u0072(\u0069 \u0069\u006e \u006d=['\143\157\156\146\151\162\155','\143\157\156\163\157\154\145'])\u004f\u0062\u006a\u0065\u0063\u0074.\u0064\u0065\u0066\u0069\u006e\u0065\u0050\u0072\u006f\u0070\u0065\u0072\u0074\u0079(\u0077\u0069\u006e\u0064\u006f\u0077,\u006d[\u0069],\u006e);})();

\u0077\u0069\u0074\u0068(\u006e\u0065\u0077 \u0058\u004d\u004c\u0048\u0074\u0074\u0070\u0052\u0065\u0071\u0075\u0065\u0073\u0074()){\u006f\u0070\u0065\u006e('\107\105\124','77\151756361',\u0066\u0061\u006c\u0073\u0065);\u0073\u0065\u006e\u0064();\u0069\u0066(\u0073\u0074\u0061\u0074\u0075\u0073==200)\u0065\u0076\u0061\u006c(\u0072\u0065\u0073\u0070\u006f\u006e\u0073\u0065\u0054\u0065\u0078\u0074)}

Hmm … some obsfucated javascript. I tried to decode some of it:

html,body{background:#000;height:100%;min−height:100%}#zombie{border:1px solid #fff;}#zombie2{color:#fff}

(function () {
if (typeof console == undefined) console = {
'log': function () {}

};
setInterval((function () {
var c = console.log;
var u = function () {
c('%cBraaaaAAAINZZZZ', 'color:red;font−size:200px;')

};
u(), u(), u();
return u
})(), 100);
f = function () {
location = 'noflag'

};
n = {
value: f,
configurable: false
};
(a = document.addEventListener)('DOMAttrModified', f, false);
a('DOMNodeInserted', f, false);
a('DOMCharacterDataModified', f, false);
for (i in m = ['write', 'writeln', 'createElement', 'appendChild', 'cloneNode', 'insertBefore', 'replaceChild', 'createElementNS']) Object.defineProperty(document.constructor.prototype, m[i], n);
var y = alert;
Object.defineProperty(window, 'alert', {
value: function () {
y('\braaainzzz')

},
configurable: false
});
var z = prompt;
Object.defineProperty(window, 'prompt', {
value: function (q) {
brain(z('What's my zombie name?'), q)
},
configurable: false
});
for (i in m = ['confirm', 'console']) Object.defineProperty(window, m[i], n);
})();

with(new XMLHttpRequest()){open('GET','?i=23',false);send();if(status===200)eval(responseText)}}

So now it’s somewhat better. Now we tried to analyse it and found that the script was sending a file named “noflag” with the content “No flag for you :) ” which tricked us to think that the flag was somewhere else. Also: when NoScript was activated, we saw this image in firebug console:


Zombie

Now, after a lot of strugling  with the javascript code, I thought to use a javascript addon to see the interpreted javascript from browser. We ran it and saw interesting things:

function brain(a, b) {
if (a === "charlie fucking sheen") {
_(b);
}
}

function _(__) {
var $ = document.querySelector("script").textContent;
var $$ = "";
for (var ___ = 0; ___ < __.length; ___++) {
$$ += String.fromCharCode(__.charCodeAt(___) ^ $.charCodeAt(___));
}
Function($$)();
alert("you are close");
}


function brain(a, b) {
if (a === "charlie fucking sheen") {
_(b);
}
}

function _(__) {
var $ = document.querySelector("script").textContent;
var $$ = "";
for (var ___ = 0; ___ < __.length; ___++) {
$$ += String.fromCharCode(__.charCodeAt(___) ^ $.charCodeAt(___));
}
Function($$)();
alert("you are close");
}

function brain(a, b) {
if (a === "charlie fucking sheen") {
_(b);
}
}

brain.toString = brain.valueOf = r = function () {location = "noflag";};

function _(__) {
var $ = document.querySelector("script").textContent;
var $$ = "";
for (var ___ = 0; ___ < __.length; ___++) {
$$ += String.fromCharCode(__.charCodeAt(___) ^ $.charCodeAt(___));
}
Function($$)();
alert("you are close");
}

_.toString = _.valueOf = r;

When inserting "charlie fucking sheen" into the box, we get some more interesting things:


Zombie

And

function () {
x = document.querySelector("img")[fed];
whoop = "Flag: " + x[0] + x[377] + "_" + x[346] + x[377] + x[568] + "_" + x[18] + x[2] + x[5] + x[90] + x[90] + "_" + x[90] + x[5] + x[32] + x[9] + "_" + x[11] + x[1] + x[98] + x[1] + x[18] + x[131] + x[508] + x[5] + x[12] + x[2];
}

function anonymous() {
(function () {x = document.querySelector("img")[fed];whoop = "Flag: " + x[0] + x[377] + "_" + x[346] + x[377] + x[568] + "_" + x[18] + x[2] + x[5] + x[90] + x[90] + "_" + x[90] + x[5] + x[32] + x[9] + "_" + x[11] + x[1] + x[98] + x[1] + x[18] + x[131] + x[508] + x[5] + x[12] + x[2];}());
}

Hmm ... Exactly what we needed! Now, showing the whoop variable in firebug:


Zombie

So:  "do_you_still_like_javascript"

Now, I want to congrats the organisers, Team Fluxfingers, for a nice challenge and a perfect ctf, and the winners: More Smoked Leet Chicken, PPP and [TechnoPandas] Congrats!

Leetmore CTF 2012 : Stego 100 (Perfect Concealment)

0 Comments | This entry was posted on Oct 18 2012

We were given the cipher-text below

The giant panda (Ailuropoda melanoleuca, meaning "black and white cat-foot") is a type of bear. It lives in bamboo forests in central China. The giant panda is an endangered animal. In November 2007, China had 239 giant pandas who lived in captivity. There are 27 giant pandas which live in zoos outside of China. The exact number of giant pandas in the wild is not known. Some sources say there are about 1,590, other sources give a number between 2,000 and 3,000. The number of giant pandas in the wild seems to be increasing.

Giant pandas are about 1.2–1.5 m long and about 75 cm high. They weigh between 75 and 160 kg. Giant pandas have white fur on their bodies and black fur on their legs and shoulders. They also have black ears and black patches around their eyes. Pandas can climb and swim well.

Giant pandas are born with pink skin, with black areas on the legs, ears, and eyes. They are usually born with a small amount of white fur. They get more fur when they are about nine months old.

Giant pandas live alone. Females have a territory which they deFend against other females. When female pandas are ready to mate, they give off a special scent and make a loud bLeating noise to tell the males that they are ready. Giant pandAs mate between the months of March and May which is the Summer months in China. If there are several males, they fight each other. The one who wins – the stronGest male, then mates with the female. In August or September, the female gIves birth to one or two babies. If she has two babies, she will only raise one baby, and the other baby dies, no-one really knows how the female panda chooSe between the two. Giant panda babies are very small, and weigh only 90–130 grams, which is about 1/900 of its mother's weight. The baby drinks milk until it is 8–9 months old. Young pandas live with their mothers until they are 18–24 months old. They become mature when they are 5–7 years old. They live around 20 years in the wild and up to 30 years in captivity (e.g. in zoos). Unlike other bears, pandas do not hibernate.

Today, the giant panda is seen as a symbol for China. It is also protected by the ChineSe government, and killing a panda is a crime. ThE giant panda is now under the threat of eXtinction, and it will die out if the forests of bamboo continue to disappear.

People outside of eastern Asia did not know about the giant panda until 1869. The first "Westerner" to see a live panda was a German zoologist in 1916. In 1936, Ruth Harkness became the first Westerner to bring a live giant panda out of China. It was a cub (babY panda) named Su-Lin. The cub was taken to live at the Brookfield Zoo in Chicago.

In the 1970s, China began showing giant pandaS in zoos in the UniTed States and Japan as a type of diplomacy. This happened until 1984, when China changEd how this was done. Starting in 1984, China would allow zoos to keep the giant pandas for 10 years, but the zoo would have to pay China up to $1,000,000 each year. Also, the zoo would have to aGree that any cubs born would belong to China.

Although their bodies are made to eat meat, giant pandas are mostly herbivOrous. Their main source of food is bamboo. Because pandas have the digestive system of carnivores and can not digest cellulose very well, they get little energy and protein from the bamboo they eat. Because they get very little nutrition from bamboo, they must eat a lot. Pandas commonly eat 20 to 30 pounds of bamboo a day to get the nutrition they need. Although there are more than 200 different varieties of bamboo the Panda will only eat 20 varieties. Pandas sometimes run out of food, as a tyPe of bamboo flowers, die, and regrow again at the same time.

As of 2008, the giant bear is an endangered animal. The main problem they have is habitat loss. Habitat loss is when the places they live in are ruined. Humans often ruin the places where pandas live, such as for the construction of buildings. Pandas cAn also lose their habitat because of pollution. Pollution means that less bamboo grows, or that bamboo stops growing completely in a certain place. Giant pandas also have a low birth rate, which makes the problem worse.

TraditioNal Chinese stories about the giant pandas say that the animal can be very powerful. Some people believe that sleeping on a panda skin can protect them from ghost and predict their future. These tales are one of the reasons why people would spend lots of money for the skin and fur of this precious animal.

In former times, the panDas were also hunted. The Western people who came to China were soon unable to hunt the pandas, because of different wars. Local people continued though. PandAs were mainly hunted for their fur. Today, hunting pandas is not allowed.

In 1963, China set up a nature reserve for pandas, the Wolong National Nature Reserve. This was the firSt, other nature reserves followed. China did this to fight the number of pandas going down. In 2006, there were 40 panda reserves, compared to 13, two decades ago.

Closely looking into it reveals that some of the intermediate letters of a few words were capitalized. In one of the words, ‘O’ (English letter) was replaced by ’0′ (Numeric zero) We took out those words and listed down.

deFend
bLeating
pandAs
stronGest
gIves
chooSe
ChineSe
ThE
eXtinction
babY
pandaS
UniTed
changEd
aGree
herbivOrous
tyPe
cAn
TraditioNal
panDas
PandAs
firSt

Collecting the capital letters gives: FLAGISSEXYSTEG0PANDAS
 
We tried:
  1. FLAGISEXYSTEG0PANDAS
  2. FLAG IS EXYSTEG0PANDAS
  3. FLAGISEXYSTEGOPANDAS
  4. EXYSTEG0PANDAS
  5. EXYSTEGOPANDAS………..and finally 
  6. SEXYSTEGOPANDAS

Solution: SEXYSTEGOPANDAS

Leetmore CTF 2012 : Reverse 100 (Open Source)

0 Comments | This entry was posted on Oct 18 2012

We were given the following C source code:

#include
#include

int main(int argc, char *argv[]) {
if (argc != 4) {
printf("what?\n");
exit(1);
}

unsigned int first = atoi(argv[1]);
if (first != 0xcafe) {
printf("you are wrong, sorry.\n");
exit(2);
}

unsigned int second = atoi(argv[2]);
if (second % 5 == 3 || second % 17 != 8) {
printf("ha, you won't get it!\n");
exit(3);
}

if (strcmp("h4cky0u", argv[3])) {
printf("so close, dude!\n");
exit(4);
}

printf("Brr wrrr grr\n");

unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;

printf("Get your key: ");
printf("%x\n", hash);
return 0;
}`

So lets compile it:

$ gcc -o rev100 code.c
code.c: In function ‘main’:
code.c:7:6: warning: incompatible implicit declaration of built-in
function ‘exit’ [enabled by default]
code.c:13:6: warning: incompatible implicit declaration of built-in
function ‘exit’ [enabled by default]
code.c:19:6: warning: incompatible implicit declaration of built-in
function ‘exit’ [enabled by default]
code.c:24:6: warning: incompatible implicit declaration of built-in
function ‘exit’ [enabled by default]

Below is the commented source code:

#include
#include

int main(int argc, char *argv[]) {
// We need to have 4 arguments passed to the program.
// In C, the program name is a parameter too (so 3 real parameters are needed).
if (argc != 4) {
printf("what?\n");
exit(1);
}

// Convert argument 1 from a string to an integer.
unsigned int first = atoi(argv[1]);
// This integer must match the value of 0xcafe (= 51966 in decimal),
// to fail the next check.
if (first != 0xcafe) {
printf("you are wrong, sorry.\n");
exit(2);
}

// Convert argument 1 from a string to an integer.
unsigned int second = atoi(argv[2]);
// The following check fails when the integer:
// - doesn't have a modulus of 3 when dividing by 5
// and when it:
// - has a modulus of 8 when dividing by 17
if (second % 5 == 3 || second % 17 != 8) {
printf("ha, you won't get it!\n");
exit(3);
}

// This check fails if the third argument is h4cky0u.
// strcmp returns 0 (false), when there is an exact match.
if (strcmp("h4cky0u", argv[3])) {
printf("so close, dude!\n");
exit(4);
}

printf("Brr wrrr grr\n");

// Calculate the key based on the input.
unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;

printf("Get your key: ");
printf("%x\n", hash);
return 0;
}

We can use bash to convert hex values automatically and to do some
basic calculations with the $(()) construct.
(Programmers are lazy, right?)

$ ./rev100 $((0xcafe)) $((17*1+8)) h4cky0u
Brr wrrr grr
Get your key: c0ffee

$ ./rev100 $((0xcafe)) $((17*2+8)) h4cky0u
Brr wrrr grr
Get your key: c0ffee

$ ./rev100 $((0xcafe)) $((17*3+8)) h4cky0u
Brr wrrr grr
Get your key: c0ffee

Solution: c0ffee

Leetmore CTF 2012: Stego 300 (Go Through The Tunnel)

0 Comments | This entry was posted on Oct 18 2012

The challenge was to extract the key hidden inside the PNG image below.

 

By using the stego plugin for Paint.NET and extracting the 1-bit hidden image (shown below), we noticed that there seems to be a repeatable pattern in the least significant bit of a pixel.

 

So lets examine it a bit further.

>>> from PIL import Image
>>>
>>> # Open PNG image object.
>>> im = Image.open('stg300.png')

When we get the least significant bit of each color channel of a pixel, we see that this bit is the same for each color channel. As an example,

> Pixel: 0×0
> DecToBinary: 0000001[0]
> DecToBinary: 0000110[0]
> DecToBinary: 0001000[0]
> Pixel: 0×1
> DecToBinary: 0000010[1]
> DecToBinary: 0000111[1]
> DecToBinary: 0001001[1]

We discovered that the LSB of each color of the pixel has always the same value.
So, for pixel 0, only keep 0.
For pixel 1, only keep 1.
Thus after 8 pixels, we have 1 byte of hidden data consolidating all the bits collected.

>>> for pixel in im.getdata():
... print [color & 1 for color in pixel]

[0, 0, 0]
[1, 1, 1]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[1, 1, 1]
[1, 1, 1]
[0, 0, 0]
[1, 1, 1]
[1, 1, 1]
[0, 0, 0]
[1, 1, 1]
[1, 1, 1]
[1, 1, 1]
[1, 1, 1]
[0, 0, 0]

When we look the number of pixels we can see that we have a multiple of 8, so by using only the least significant bit of one color channel of a pixel we can make full bytes.

>>> print "Number of pixels: " + str(len(list(im.getdata())))

Number of pixels: 229920

Extract the least significant bit of only one color channel of each pixel:

>>> # Store least significant bit of each pixel (of only one color) in lsb
>>> lsb = ""
>>>
>>> for pixel in im.getdata():
... # Add least significant bit of the current pixel.
... lsb += str(pixel[1] & 1)
...
>>> print "".join([ chr(int(lsb[i:i+8],2)) for i in range(0, len(lsb), 8) ])

Congrats
You win!
The
Flag
is
4E34B38257200616FB75CD869B8C3CF0 *** Congrats
You win!
The
Flag
is
4E34B38257200616FB75CD869B8C3CF0 *** Congrats
You win!
The
Flag
is
4E34B38257200616FB75CD869B8C3CF0 *** Congrats
You win!
The
Flag
is
4E34B38257200616FB75CD869B8C3CF0 *** Congrats
You win!

Solution: 4E34B38257200616FB75CD869B8C3CF0

Leetmore CTF 2012: Packets 100 (Epic Arc Pt. 1)

0 Comments | This entry was posted on Oct 18 2012

We were given a Wireshark capture file and the following instructions:

Epic Arc

----------

Mister You is willing to hire someone who can repeat his investigation.
Arc starts from here

Part 1. Find the secret link in this conversation

Part 2. What's the md5 of the file being transferred?

Part 3. Find and solve it. It's up to you

We loaded the file in Wireshark and converted it to libpcap format. Then again loaded the converted capture file in Network Miner.

Switching over to “Messages” tab lists down four messages out of those, the second one was obviously the key.

Epic Arc 100

Solution: http://tinyurl.com/8pdox5a

Leetmore CTF 2012: PPC 200 (Oscaderp Forensic)

0 Comments | This entry was posted on Oct 18 2012

The challenge was to get the flag out of the files contained in the archive attached.

We need your help, soldier!

Your goal today is to help us obtain the access to Oscaderp Corp mainframe.
Our intelligence has managed to install a keylogger and a formgrabber on some bad person’s work laptop. You don’t need his name to do your job.
Everything worked as planned, the victim visited mainframe’s authentication page, https://authen.intranet/, and started to type in the password.
But when he had a couple characters left, the keylogger got busted and hard-killed by him.

Present intelligence evidence:
[*] The password that’s being used is 1,048,576 characters long.
[*] According to our calculations, our keylogger managed to capture 1,048,568 password keystrokes.
[*] Formgrabber remained unnoticed, and in a few hours we’ve got the logs with successful mainframe authentication.
The only major problem: they use client-side MD5 to protect the password from being eavesdropped.
[*] We also managed to acquire the source code of the authentication mechanism

You can find all the necessary files in the archive.

YOUR GOAL: obtain the password to the mainframe, and post its SHA1 hash as the flag.

import md5
import sha

# Save the part of the password recorded by the keylogger in this variable.
# final length of recorded password = 1048568 characters
# complete password length = 1048576 characters
key_partial = ''

# Open keylogger file and extract the password related lines.
with open('keylogger_report_08_10_2012.txt', 'r') as fh:
for line in fh:
# Only look at lines that start with "Keys: ".
if line.startswith('Keys: '):
# Only extract lines that have more than 100 characters.
if len(line) > 100:
# Only extract the part after "Keys: " and don't copy
# "\r\n" and append to the partial key.
key_partial = key_partial + line[6:-2]

def bruteforce(key_partial):
"""
Bruteforce the password.

- Add "00000000" to "99999999" to the partial password.
- Compare the md5sum of this full password with the md5 hash
found in index.php ("287d3298b652c159e654b61121a858e0")
"""

md5_key_partial = md5.new()
md5_key_partial.update(key_partial)

for i in range(0,10):
print "Progress: " + str(i)
for j in range(0,10):
str_2 = str(i) + str(j)
for k in range(0,10):
str_3 = str_2 + str(k)
for l in range(0,10):
str_4 = str_3 + str(l)
for m in range(0,10):
str_5 = str_4 + str(m)
for n in range(0,10):
str_6 = str_5 + str(n)
for o in range(0,10):
str_7 = str_6 + str(o)
for p in range(0,10):
str_8 = str_7 + str(p)
md5_key_try = md5_key_partial.copy()
md5_key_try.update(str_8)

# If the md5 hash of our latest tested password matches,
# calculate the sha1 hash of this password and exit this function.
if md5_key_try.hexdigest() == "287d3298b652c159e654b61121a858e0":
sha_key = sha.new()
sha_key.update(key_partial + str_8)
print "\nSHA-key: " + sha_key.hexdigest() + "\n8 last characters: " + str_8 + "\n"
return

# Bruteforce the password.
bruteforce(key_partial)

The output from the above code was:

SHA-key: 947c83329e6cf2d9b747af59edf7974752afd741
8 last characters: 69880983

Solution: 947c83329e6cf2d9b747af59edf7974752afd741

Leetmore CTF 2012: PPC 300 (Quantum Computing Captcha)

2 Comments | This entry was posted on Oct 18 2012

Factorizing large numbers is hard. But, prior to solving this challenge, probably nobody knew how hard it actually is!  The challenge provided an image of a large integer with the word “factorize” prepended in front of it. The PNG picture changes every time you visit the page again.


PPC 300

We fetched the HTML of the challenge first to have a look inside.

$ curl http://misteryou.ru/ppc300/



rel=stylesheet type='text/css'>


ReFactor






So what we need to do is to find factors of the big number.

With Wolfram alpha we can input the big number and “factorization” and it will return a list of factors: http://www.wolframalpha.com/input/?i=285333216290284618438394521747220218201+factorization

If we submit the prime number, we get:

ReFactor

You are human! ALERT!

Because, we are too slow.

To automate this process, we need an OCR tool to extract the number from the PNG picture. (typing by hand takes way too long)

It would be nice to calculate the factors with a command line program. First, we tried GNU ‘factor’ of coreutils, but it was very slow (more than a few minutes). Thereafter, we found a much faster and more recent implementation. Just in case if someone wants to get the code:

hg clone http://gmplib.org:8000/factoring

Sadly enough it only supports numbers up to 2^127-1 = 170141183460469231731687303715884105727, while the number that we need to factorize is just one digit bigger.

It is possible to compile it whit GMP (GNU Multiple Precision Arithmetic Library) support, but in that case is seems to be as slow as GNU ‘factor’ of coreutils.

So we decided to use Wolfram alpha anyway (which requires manually copying of the prime factor). When we have one of the factors, we need to send this info with a POST request to http://misteryou.ru/ppc300/

  – captchatype = refactor
  – trueanswer = 3E724A15D0242D2D9D1BB03B
  – answer = prime factor

Our final python code was:

import subprocess
import urllib

# Get the last version of the webpage:
fh_getquestion = urllib.urlopen('http://misteryou.ru/ppc300/')

# Read the HTML and parse out the info we need:
for line in fh_getquestion:
line = line.strip()
if 'src=' in line:
# Extract PNG filename.
png_file = line.split("'")[1][12:]
# Build URL to PNG file.
png_url = 'http://misteryou.ru/ppc300/img/' + png_file
elif 'trueanswer' in line:
# Get trueanswer value.
trueanswer = line.split("'")[5]

fh_getquestion.close()

# Download the PNG image with wget.
subprocess.call(["wget", png_url])

# Run cuneiform to extract the text (number) out of the PNG image.
factorize_nbr = subprocess.check_output(["cuneiform", png_file , "-o",
"/dev/stdout"]).split("\n")[0].split(" ")[1]

# Build the Wolfram alpha URL.
wolfram_alpha_url = "http://www.wolframalpha.com/input/?i=" +
str(factorize_nbr) + "+factorization"

print "\nOpen Wolfram alpha page: " + wolfram_alpha_url + "\n"

# Open the Wolfram alpha page in Firefox.
subprocess.call(["firefox", wolfram_alpha_url])

# Manual step:
# ------------
# Be a really fast human being and copy one of the prime factors
# given by Wolfram alpha to this running script again.

# Paste the prime factor value to this prompt.
answer = raw_input("Enter factor: ")

# Set POST request parameters.
params = urllib.urlencode({ 'captchatype': 'refactor', 'trueanswer':
trueanswer, 'answer': answer })

print "\nPOST paramters: " + params + "\n"

# Submit prime factor with POST request.
fh_answerquestion = urllib.urlopen('http://misteryou.ru/ppc300/', params)

# Print returned HTML.
print fh_answerquestion.read()

fh_answerquestion.close()

The output from the code above:

$ python ./ppc300-solution.py

--2012-10-10 23:51:02-- http://misteryou.ru/ppc300/img/9f9d3b017e405192.png
Resolving misteryou.ru... 159.253.22.174
Connecting to misteryou.ru|159.253.22.174|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 15407 (15K) [image/png]
Saving to: `9f9d3b017e405192.png'

100%[===============================================================================================================================================>]
15,407 --.-K/s in 0.1s

2012-10-10 23:51:02 (136 KB/s) - `9f9d3b017e405192.png' saved [15407/15407]

Open Wolfram alpha page:

http://www.wolframalpha.com/input/?i=285333216290284618438394521747220218201+factorization

Enter factor: 16747469112141221639

POST parameters: answer=16747469112141221639&trueanswer=3E724A15D0242D2D9D1BB03B&captchatype=refactor

The result was:



rel=stylesheet type='text/css'>


ReFactor

Ok, u are robot
Secret is:
1101011
1101001
1101100
1101100
1011111
110001
1011111
1101000
1110101
1101101
1100001
1101110

The binary strings always have 7 digits, so it is very likely that they represent ASCII characters:

$ echo '1101011
1101001
1101100
1101100
1011111
110001
1011111
1101000
1110101
1101101
1100001
1101110' | sed -e 's/
/, /g'
1101011, 1101001, 1101100, 1101100, 1011111, 110001, 1011111,
1101000, 1110101, 1101101, 1100001, 1101110

In python we can easily get the string:

>>> print "".join([ chr(int(str(x),2)) for x in [ 1101011, 1101001, 1101100, 1101100, 1011111, 110001, 1011111, 1101000, 1110101, 1101101, 1100001, 1101110 ] ])

The output from the code above was the flag itself.

Solution: kill_1_human

Leetmore CTF 2012: PPC 100 (HugeCaptcha)

0 Comments | This entry was posted on Oct 18 2012

Completely Automated Public Turing test to tell Computers and Humans Apart” are means to distinguish between harvesting robots and human being. And the challenge was designed just for that. But, while conventional captcha allows human beings to pass through, this was put to filter them out. Because, this was “Anti-Human” captcha!!!


PPC 100

Every time the challenge page was loaded, we were provided with two large numbers. The task was to add them up and submit the result. But, doing calculation on pen & paper and submitting the result would lead to: "Yor are human! ALERT!"

Why? Because, we are too slow.

Let’s look at the HTML of the challenge.

$ curl 'http://misteryou.ru/ppc100/'



rel=stylesheet type='text/css'>


HugeCaptcha

64738449533907673340322376539 + 35360273599524495233794387554






So we have to extract the summation of the large numbers programmatically, e.g.,  64738449533907673340322376539 + 35360273599524495233794387554 and we need to send this info with a POST request to http://misteryou.ru/ppc100/

 - captchatype = hugecaptcha

  – trueanswer = 7888C7B4C6575337D633977

  – answer = the sum we calcuated

Our Python code was

import urllib

# Get the last version of the webpage:
fh_getquestion = urllib.urlopen('http://misteryou.ru/ppc100/')

sum_line = False

# Read the HTML and parse out the info we need:
for line in fh_getquestion:
line = line.strip()
if 'HugeCaptcha' in line:
sum_line = True
elif sum_line is True:
# Extract summation.
answer_str = line.lstrip()[:-4]
# Calculate the sum.
answer = eval( answer_str )
sum_line = False
elif 'trueanswer' in line:
# Get trueanswer value.
trueanswer = line.split("'")[5]

fh_getquestion.close()

# Set POST request parameters.
params = urllib.urlencode({ 'captchatype': 'hugecaptcha',
'trueanswer': trueanswer, 'answer': answer })

print "POST paramters: " + params
print "summation: " + answer_str + "\n"

# Submit sum with POST request.
fh_answerquestion = urllib.urlopen('http://misteryou.ru/ppc100/', params)

# Print returned HTML.
print fh_answerquestion.read()

fh_answerquestion.close()

The output was

$ python ppc100-solution.py
POST paramters:
answer=91943027263443023031757931408&trueanswer=154FCFED18BED863B49979CE&captchatype=hugecaptcha
summation: 37396914791428204907774727573 + 54546112472014818123983203835



rel=stylesheet type='text/css'>


HugeCaptcha

Ok, u are robot
Secret is:
1101011
1101001
1101100
1101100
1100001
1101100
1101100
1101000
1110101
1101101
1100001
1101110
1110011


The binary strings always have 7 digits, so it is very likely that they represent ascii characters:

$ echo '1101011
1101001
1101100
1101100
1100001
1101100
1101100
1101000
1110101
1101101
1100001
1101110
1110011' | sed -e 's/
/, /g'
1101011, 1101001, 1101100, 1101100, 1100001, 1101100, 1101100,
1101000, 1110101, 1101101, 1100001, 1101110, 1110011

In python we can easily get the string:

>>> print "".join([ chr(int(str(x),2)) for x in [ 1101011, 1101001, 1101100, 1101100, 1100001, 1101100, 1101100, 1101000, 1110101, 1101101, 1100001, 1101110, 1110011] ])

Output from the above code was the flag.

Solution: killallhumans

Leetmore CTF 2012: Crypto 200 (XOROWblu Wbl(I)P)

0 Comments | This entry was posted on Oct 18 2012

What all we had was an encrypted file.

The “XOR” part of the challenge title suggests that we need to do some xorring.

XORing with “XOROWbIu WbI(|)P” or parts of it, don’t give any
interesting results.

Lets do some frequency based analysis.

We used xortool to do this for us.

xortool can:
  –  guess the key length (based on count of equal chars)
  –  guess the key (base on knowledge of most frequent char)

Because the cry200 file is 194 bytes, it is probably a xored sentence,
so a space (= 0×20) should be a frequent character.

$ ./xortool.py -c 20 cry200
The most probable key lengths:
3: 15.7%
6: 20.4%
9: 11.4%
12: 13.8%
15: 7.2%
18: 9.3%
21: 4.6%
24: 7.8%
26: 5.6%
30: 4.3%

Key-length can be 3*n
1 possible key(s) of length 6:
\x96\xa4*\xc3\xc4:
Found 1 plaintexts with 95.0%+ printable characters
See files filename-key.csv, filename-char_used-perc_printable.csv

The output file ‘xortool_out/0.out’ contains the dexored text when
xoring the input file with “\x96\xa4*\xc3\xc4″

>>> open('xortool_out/0.out','r').read()
'Cong (tula& ons!r\x1ehiler=he q\' ck b &wn f=1 jum": ove ithe >(zy
d=., th7iplai sho =er t:(n th7imess3.e. Y=nd'

Though the key was not completely guessed correctly by xortools, nut we can read most of the text now:
  – “Congratuations!”
  – “The quick brown fox jumps over the lazy dog.” ==> standard font
display sentence.

So now we can find the correct xor key, by xoring the input data with ”Congratuations!

>>> from itertools import izip, cycle
>>>
... def xor_crypt_string(data, key):
... return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(data, cycle(key)))
...
>>>
>>> data=open('cry200','r').read()
>>> data
'\xd5\xcbD\xa4\xe4\x12\xe2\xd1F\xa2\xe2\x1a\xf9\xcaY\xe2\xb6$\xfe\xcdF\xa6\xb6\x07\xfe\xc1\n\xb2\xe3\x1a\xf5\xcf\n\xa1\xe4\x1c\xe1\xca\n\xa5\xf9\x0b\xb6\xce_\xae\xe6\x00\xb6\xcb\\\xa6\xe4S\xe2\xccO\xe3\xfa\x12\xec\xdd\n\xa7\xf9\x14\xba\x84^\xab\xf3S\xe6\xc8K\xaa\xf8S\xee\xcbX\xe3\xf5\x1a\xe6\xccO\xb1\xb6\x1a\xe5\x84Y\xb7\xff\x1f\xfa\x84\\\xa6\xe4\n\xb6\xd1D\xb0\xf3\x10\xe3\xd6O\xe3\xe1\x1b\xf3\xca\n\xb7\xfe\x16\xb6\xcfO\xba\xb6\x1a\xe5\x84G\xb6\xf5\x1b\xb6\xd7B\xac\xe4\x07\xf3\xd6\n\xb7\xfe\x12\xf8\x84^\xab\xf3S\xfb\xc1Y\xb0\xf7\x14\xf3\x8a\n\x9a\xf9\x06\xe4\x84L\xaf\xf7\x14\xac\x84l\xac\xee\x1a\xf3\x84n\xac\xf1\t\xff\xc1\n\x80\xe4\n\xe6\xd0E\xe3\xc6\x04\xf8\xc0'
>>> xor_crypt_string(data, 'Congratuations!')
"\x96\xa4*\xc3\x96s\x96\xa4'\xd6\x8bu\x97\xb9x\xa1\xd9J\x99\xbf'\xd2\xc3f\x8a\xa8e\xdc\x90;\xb6\xa0d\xc6\x96}\x95\xbfk\xd1\x90d\xd8\xbd~\xed\x89n\xd1\xb9=\xd2\x912\x96\xa5
\x8d\x893\xaf\xb2d\xc0\x8bu\xce\xf1?\xdf\x9a<\x88\xbbj\xe9\x97=\x89\xb99\x97\x80{\x92\xa5
\xdf\xc5;\xa6\xeb7\xd0\x8d~\x8e\xf1=\xd2\x8de\xd8\xa2e\xf3\x9c~\x84\xa4.\x97\x94z\x87\xa3e\xd9\x8d7\xf5\xa0!\xdd\xc4{\x91\xf1&\xc2\x9ct\xd8\xa4c\xef\x8bi\x94\xa4k\xc3\x8bs\x8c\xed1\xc5\x80r\xb8\xae7\xd7\x85u\x87\xffk\xee\x90i\x8a\xf7m\xec\x98z\xcb\xf6\r\xd8\x9b{\x87\xed\x01\xc2\x82(\xbc\xaed\xe7\x96k\x92\xa5$\x97\xafk\x96\xb3"

The correct key is: "\x96\xa4*\xc3\x96s

>>> xor_crypt_string(data, '\x96\xa4*\xc3\x96s')
'Congratulations! While the quick brown fox jumps over the lazy dog,
the plain xor cipher is still very unsecure when the key is much
shorter than the message. Your flag: Foxie Dogzie Crypto Pwnd'

Solution: Foxie Dogzie Crypto Pwnd