Apr 19, 2016
A week ago, I took part of the sCTF.io 2016 Q1 with a friend. This is the first time I take seriously part to a CTF. We managed to score 1700 points and ranked 7th of about 700 teams (Funpetitor for us as we do not live in US).
Here are a few notes about problems we solved and found interesting.
I used IDA during the challenge, but as I want to practice with Radare2 the write up will be using radare2.
First, I ran the program to have an idea of what to look for.
./rev1
What is the magic password?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
So it does not print anything if the password is wrong. It’s time to fire radare2 !
[0x00400560]> aaa
[0x00400560]> iz
vaddr=0x00400744 paddr=0x00000744 ordinal=000 sz=28 len=27 section=.rodata type=a string=What is the magic password?
vaddr=0x00400763 paddr=0x00000763 ordinal=001 sz=26 len=25 section=.rodata type=a string=Correct! Your flag is: %s
[0x00400560]> pd 10 @ 0x00400744-5
| 0x0040073f 0001 add byte [rcx], al
| ;-- sym._IO_stdin_used:
| ;-- section..rodata:
| 0x00400740 0100 add dword [rax], eax ; [14] va=0x00400740 pa=0x00000740 sz=61 vsz=61 rwx=-r-- .rodata
| 0x00400742 0200 add al, byte [rax]
| ; DATA XREF from 0x00400677 (sym.main)
| ;-- str.What_is_the_magic_password_:
| 0x00400744 .string "What is the magic password?" ; len=28
| | ; DATA XREF from 0x00400688 (sym.main)
| 0x00400760 256400436f and eax, 0x6f430064
| | ; DATA XREF from 0x004006a8 (sym.main)
| ;-- str.Correct__Your_flag_is:__s: ; ignore this__
| 0x00400763 .string "Correct! Your flag is: %s" ; len=26
;-- section_end..rodata:
0x0040077d 0000 add byte [rax], al
0x0040077f 0001 add byte [rcx], al
;-- section..eh_frame_hdr:
0x00400780 011b add dword [rbx], ebx ; [15] va=0x00400780 pa=0x00000780 sz=52 vsz=52 rwx=-r-- .eh_frame_hdr
0x00400782 033b add edi, dword [rbx]
[0x00400560]> s 0x00400677
[0x00400677]> pdf @ 0x00400677
/ (fcn) sym.main 104
| ; var int local_0_4 @ rbp-0x4
| ; var int local_1 @ rbp-0x8
| ; var int local_2 @ rbp-0x10
| ; DATA XREF from 0x0040057d (entry0)
| ;-- main:
| ;-- sym.main:
| 0x00400656 55 push rbp
| 0x00400657 4889e5 mov rbp, rsp
| 0x0040065a 4883ec10 sub rsp, 0x10
| 0x0040065e c745fc000000. mov dword [rbp-local_0_4], 0
| 0x00400665 48b868347830. movabs rax, 0x2121217230783468
| 0x0040066f 488945f0 mov qword [rbp-local_2], rax
| 0x00400673 c645f800 mov byte [rbp-local_1], 0
| 0x00400677 bf44074000 mov edi, str.What_is_the_magic_password_ ; "What is the magic password?" @ 0x400744
| 0x0040067c e8affeffff call sym.imp.puts ;sym.imp.puts()
| 0x00400681 488d45fc lea rax, qword [rbp-local_0_4]
| 0x00400685 4889c6 mov rsi, rax
| 0x00400688 bf60074000 mov edi, 0x400760
| 0x0040068d b800000000 mov eax, 0
| 0x00400692 e8b9feffff call sym.imp.scanf ;sym.imp.scanf()
| 0x00400697 8b45fc mov eax, dword [rbp-local_0_4]
| 0x0040069a 3d745b0000 cmp eax, 0x5b74
| ,=< 0x0040069f 7516 jne 0x4006b7
| | 0x004006a1 488d45f0 lea rax, qword [rbp-local_2]
| | 0x004006a5 4889c6 mov rsi, rax
| | 0x004006a8 bf63074000 mov edi, str.Correct__Your_flag_is:__s ; "Correct! Your flag is: %s" @ 0x400763
| | 0x004006ad b800000000 mov eax, 0
| | 0x004006b2 e859feffff call sym.imp.printf ;sym.imp.printf()
| | ; JMP XREF from 0x0040069f (sym.main)
| `-> 0x004006b7 b800000000 mov eax, 0
| 0x004006bc c9 leave
\ 0x004006bd c3 ret
[0x00400677]> q
The steps :
aaa
: First, analyze all the program fonctionsìz
: Then print all the strings of the programpd 10 @ 0x00400744-5
: We show the disassembly around one of the strings. We see that “What is the magic password” is used in main
at 0x00400677s 0x00400677
: So we move to 0x00400677pdf @ 0x00400677
: And we print the disassembly of the function (main).0x5b74
as a numerical value. The password is then this value in decimal : 23412We found our password, let’s feed it to the binary :
./rev1
What is the magic password?
23412
Correct! Your flag is: h4x0r!!!
We did it !
flag : sctf{h4x0r!!!}
Write Up To-Do, the website is currently off-line. (If I remember well, I added an input to the form).
A quick overview of the file gives us no hint, except that the lines are quite lengthy. Looking at the length, we see that the length of the lines is always under 127, and could always be a printable ascii char.
We wrote this code to convert the length of the lines to characters :
$file = file_get_contents("encrypted.dat");
$file = explode("\n", $file);
foreach($file as $line){
echo chr(strlen($line));
}
echo "\n";
Which gave us the flag : sctf{101_th3_numb3r5_d1dn'7_3v3n_m4tt3r}
For this one, I realized my method was wrong, but I could not think of any other way of doing it :
The file is a record of the rain in the wav format. Let’s see if the file contains other data :
binwalk rain.wav
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
152318 0x252FE MySQL ISAM compressed data file Version 2
586865 0x8F471 MySQL ISAM compressed data file Version 5
5669358 0x5681EE MySQL ISAM index file Version 1
5831936 0x58FD00 TIFF image data, little-endian offset of first image directory: 8
5832467 0x58FF13 Unix path: /www.w3.org/1999/02/22-rdf-syntax-ns#">
5832624 0x58FFB0 Unix path: /purl.org/dc/elements/1.1/"
5832748 0x59002C Unix path: /ns.adobe.com/xap/1.0/mm/"
5832806 0x590066 Unix path: /ns.adobe.com/xap/1.0/sType/ResourceEvent#">
There we see that there is a TIFF header at 0x58FD00. So I extract it with dd if=rain.wav of=writeup.tiff bs=1 skip=5831936
which gives me this image :
We can clearly recognize a QR Code, but it looks scrambled.
After a few minutes of paint, I achieved reconstructing the original QR Code which gives me the flag : sctf{5t3g0n4gr4phy_i5_fun_r1t3?}
The file is a TuxGuitar tablature. Once opened, we see only two notes, by group of 15 with 5 notes per measure. It looks like morse, so we wanted to transcript this to dots for the E and dashes for the C notes. As they are a lot of notes, we decided to do this automagically.
:%v/fret/d
:%s/<fret>//g
and :%s/<\/fret>//g
and :%s/\n//g
:%s/5/1/g
:%s/2/./g
and :%s/1/-/g
qa
four times right arrow
then a <space> <esc> <right arrow> q
and finally 145@a
(145 because they are 730 char).Finally, the flag : sctf{1_u53d_t0_m4k3_mu51c_w1th_th15_4ll_th3_t1m3}
Once again we’ll use Radare2 :
[0x00400560]> aaa
[0x00400560]> iz
vaddr=0x004007a4 paddr=0x000007a4 ordinal=000 sz=28 len=27 section=.rodata type=a string=What is the magic password?
vaddr=0x004007c3 paddr=0x000007c3 ordinal=001 sz=26 len=25 section=.rodata type=a string=Correct! Your flag is: %d
[0x00400560]> pd 10 @ 0x004007a4 -5
| 0x0040079f 0001 add byte [rcx], al
| ;-- sym._IO_stdin_used:
| ;-- section..rodata:
| 0x004007a0 0100 add dword [rax], eax ; [14] va=0x004007a0 pa=0x000007a0 sz=61 vsz=61 rwx=-r-- .rodata
| 0x004007a2 0200 add al, byte [rax]
| ; DATA XREF from 0x0040066c (sym.main)
| ;-- str.What_is_the_magic_password_:
| 0x004007a4 .string "What is the magic password?" ; len=28
| ||| ; DATA XREF from 0x0040067d (sym.main)
||| 0x004007c0 256400436f and eax, 0x6f430064
| ||| ; DATA XREF from 0x00400702 (sym.main)
||| ;-- str.Correct__Your_flag_is:__d: ; ignore me__
||| 0x004007c3 .string "Correct! Your flag is: %d" ; len=26
||||| ;-- section_end..rodata:
||||| 0x004007dd 0000 add byte [rax], al
||||| 0x004007df 0001 add byte [rcx], al
||||| ;-- section..eh_frame_hdr:
||||| 0x004007e0 011b add dword [rbx], ebx ; [15] va=0x004007e0 pa=0x000007e0 sz=52 vsz=52 rwx=-r-- .eh_frame_hdr
||||| 0x004007e2 033b add edi, dword [rbx]
[0x00400560]> s 0x00400702
[0x00400702]> pdf @ main
/ (fcn) sym.main 194
| ; arg int arg_64043_5 @ rbp+0x7d15d
| ; var int local_0_4 @ rbp-0x4
| ; var int local_1 @ rbp-0x8
| ; DATA XREF from 0x0040057d (entry0)
| ;-- main:
| ;-- sym.main:
| 0x00400656 55 push rbp
| 0x00400657 4889e5 mov rbp, rsp
| 0x0040065a 4883ec10 sub rsp, 0x10
| 0x0040065e c745f8000000. mov dword [rbp-local_1], 0
| 0x00400665 c745fc5dd107. mov dword [rbp-local_0_4], 0x7d15d ; [0x7d15d:4]=-1
| 0x0040066c bfa4074000 mov edi, str.What_is_the_magic_password_ ; "What is the magic password?" @ 0x4007a4
| 0x00400671 e8bafeffff call sym.imp.puts
| ^- sym.imp.puts()
| 0x00400676 488d45f8 lea rax, qword [rbp-local_1]
| 0x0040067a 4889c6 mov rsi, rax
| 0x0040067d bfc0074000 mov edi, 0x4007c0
| 0x00400682 b800000000 mov eax, 0
| 0x00400687 e8c4feffff call sym.imp.scanf
| ^- sym.imp.scanf()
| 0x0040068c 8b45f8 mov eax, dword [rbp-local_1]
| 0x0040068f 3d83da0d03 cmp eax, 0x30dda83
| ,=< 0x00400694 757b jne 0x400711
| | 0x00400696 8b45f8 mov eax, dword [rbp-local_1]
... lot of ops ...
| | 0x00400700 89c6 mov esi, eax
| | 0x00400702 bfc3074000 mov edi, str.Correct__Your_flag_is:__d ; "Correct! Your flag is: %d" @ 0x4007c3
| | 0x00400707 b800000000 mov eax, 0
| | 0x0040070c e8fffdffff call sym.imp.printf
| | ^- sym.imp.printf()
| | ; JMP XREF from 0x00400694 (sym.main)
| `-> 0x00400711 b800000000 mov eax, 0
| 0x00400716 c9 leave
\ 0x00400717 c3 ret
We used the same way we used for Rev1 : look for strings, look where they are used, read the code around.
This time the code was very easy to understand : right after the scanf
there is a comparison then a jump. It jumps only if the password is not equal to 0x30dda83
. So we feed it as a base 10 number to the binary.
./rev2
What is the magic password?
51239555
Correct! Your flag is: 51196695
That’s it : flag : sctf{51196695}
We have a jar file which displays a login/register page, once logged in, we can set text that will be saved (securely).
A jar file is an archive, so we can unzip it and try to find useful informations into the files.
unzip Secure_Text_Saver.jar
Archive: Secure_Text_Saver.jar
inflating: META-INF/MANIFEST.MF
inflating: Account.class
creating: com/
creating: com/intellij/
creating: com/intellij/uiDesigner/
creating: com/intellij/uiDesigner/core/
inflating: com/intellij/uiDesigner/core/AbstractLayout.class
inflating: com/intellij/uiDesigner/core/DimensionInfo.class
inflating: com/intellij/uiDesigner/core/GridConstraints.class
inflating: com/intellij/uiDesigner/core/GridLayoutManager.class
inflating: com/intellij/uiDesigner/core/HorizontalInfo.class
inflating: com/intellij/uiDesigner/core/LayoutState.class
inflating: com/intellij/uiDesigner/core/Spacer.class
inflating: com/intellij/uiDesigner/core/SupportCode$TextWithMnemonic.class
inflating: com/intellij/uiDesigner/core/SupportCode.class
inflating: com/intellij/uiDesigner/core/Util.class
inflating: com/intellij/uiDesigner/core/VerticalInfo.class
inflating: Login_Page.class
ls
Account.class com Login_Page.class META-INF Secure_Text_Saver.jar
strings Login_Page.class
... lot of text ...
Login_Page.java
Account
ztaylor54
]!xME}behA8qjM~T
javax/swing/JFrame
Login | Sign Up
... lot of text ...
While reading the strings found in the Login_Page.class
file, we found the nickname of the author of the challenge, we tried this username and the following line as password. And it worked !
flag : sctf{w0w_th4t_w45_pr377y_e45y}
The same way we opened the previous jar, we unzip it and look at the main class file.
We then look for strings in it.
unzip cookie.jar
Archive: cookie.jar
inflating: META-INF/MANIFEST.MF
creating: com/
creating: com/intellij/
creating: com/intellij/uiDesigner/
creating: com/intellij/uiDesigner/core/
... Lot of Text ...
inflating: Cookie.class
ls
com Cookie.class cookie.jar META-INF
strings Cookie.class
Cookie
java/lang/Object
Cookie.java
... Lot of text ...
java/io/PrintStream
println
fdf87a05e2169b88a8db5a1ebc15fa50
equals
(Ljava/lang/Object;)Z
success! it's working!
... Lot of text ...
A quick overview allows us to find a md5 hash. A simple search with google gives us the plain text : thisisaverystrongpassword
We then can input this password into the application, which gives us the following flag : flag: sctf{g3t_y0ur_h4nd_0ut_0f_my_c00k13_j4r!}
For this challenge, we have to trilaterate a few devices’ position knowing their distance from 4 satellites and the satelittes’ position.
In the description.txt
file we have the equations between the position (x,y,z) and the distances (d1, d2, d3, d4) to the satellites. I rewrote those equations a little to have simple equations for the coordinates.
I wrote a small script in PHP for this task :
$data = file_get_contents("tracking.in");
$data = explode("---------------------------------", $data);
$p = 2000; // x coordinate of satellite B
$q = 2000; // x coordinate of satellite C
$r = 2000; // y coordinate of satellite C
$s = 3000; // x coordinate of satellite D
$t = 1500; // y coordinate of satellite D
$u = 1700; // z coordinate of satellite D
$data = explode("\n",$data[1]);
array_pop($data);
array_shift($data);
$x_tot = 0;
$y_tot = 0;
$z_tot = 0;
foreach($data as $k => $l){
list($d1, $d2, $d3, $d4) = explode(" ", $l);
// Compute x y and z
$x = ($d1*$d1 - $d2 * $d2 + $p*$p)/(2*$p);
$y = -1*($d3*$d3 - $d2 * $d2 - 2*$x*($p-$q) - $q * $q + $p * $p - $r * $r)/(2 * $r);
$z = -1*($d4*$d4 - $d1 * $d1 + 2 * $x * $s - $s * $s + 2 * $t * $y - $t * $t - $u * $u)/(2 * $u);
$x_tot += $x;
$y_tot += $y;
$z_tot += $z;
}
echo "FINAL : sctf{".ceil($x_tot/count($data)).", ".ceil($y_tot/count($data)).", ".ceil($z_tot/count($data))."}\n";
Which computes the flag : flag: sctf{537, 516, 487}
In this challenge, we have an image with text encoded in it. Each byte is coded with a 7 bits binary code and a color. Depending of the color, the number represented by the black and white is shifted or not.
I wrote two functions, one to get the shift value depending of the color of the block, another one to get the value of the number represented by the 7 bits binary code.
Then I read the image from top to bottom and convert the numbers to ascii.
function getColorCode($color){
$r = $color['r'];
$g = $color['g'];
$b = $color['b'];
$a = $color['a'];
if($r == 255 && $g == 0 && $b == 0)
return 0;
if($r == 128 && $g == 0 && $b == 128)
return 1;
if($r == 0 && $g == 0 && $b == 255)
return 2;
if($r == 0 && $g == 128 && $b == 0)
return 3;
if($r == 255 && $g == 255 && $b == 0)
return 4;
if($r == 255 && $g == 165 && $b == 0)
return 5;
echo "Erreur : $r - $g - $b\n";
}
function getCode($image, $line){
global $width, $ps;
$curwidth = $width/4*2+$ps/2;
$val = 0;
$i = 6;
while($curwidth < $width){
$color = $image->getImagePixelColor($curwidth, $line);
$color = $color->getColor();
if($color['r'] == 0)
$val += pow(2,$i);
$i--;
$curwidth += $ps;
}
return $val;
}
$image = new Imagick("code1.png");
$ps = 12; // Pixel Size for code1.png;
$width = 168;
$height = 12900;
$currentline = $ps/2;
$chars = [];
while($currentline < $height){
$color = $image->getImagePixelColor($width/4, $currentline);
$color = $color->getColor();
$color = getColorCode($color);
$code = getCode($image, $currentline);
//echo "Color : $color - code : $code\n";
$chars[] = chr($code-$color);
$currentline += $ps;
}
$str = implode($chars);
echo $str;
This scripts outputs this string :
Joe Lopo was a man of mild temperament ... too much text ... Lobo into a lair of pitch blackness found to be a parallel dimension that caus ABCiamtheflagalllowercasenojokeDEF
anyone whose first name began with J along with M L and Q to become rather uncomfortable Joe was also suddenly introduced to undroclamaticolomphasisciousy the eccentric tapeworm with a strong morrocanaccent I mundroclamaticolomphasisciousy the eccentric tapeworm I like pizza so how are ya doin I have no idea said Joe
I added by hand spaces to make it more understandable. And we have our flag : flag: sctf{iamtheflagalllowercasenojoke}
Vertinet is following the Verticode challenge as it uses the same coding for strings but we need to communicate with a server and to solve a few of them automatically to solve it.
This one took a little more time as I had difficulties communicating with the server. I used the same code as in Verticode, and wrapped the main code into a new function decode
which takes an image and outputs the decoded string. I also added a resolve
function to split the answer I received from the server and keep only the interesting part : the image.
Then I added the connection to the challenge server and the automatic resolve and answer to the server. I added logging to follow what is going on between my script and the server.
function decode($image){
$geom = $image->getImageGeometry();
$width = $geom['width'];
$height = $geom['height'];
$ps = $width / 2 / 7;
$currentline = $ps/2;
$chars = [];
while($currentline < $height){
$color = $image->getImagePixelColor($width/4, $currentline);
$color = $color->getColor();
$color = getColorCode($color);
$code = getCode($image, $currentline, $width, $ps);
//echo "Color : $color - code : $code\n";
$chars[] = chr($code-$color);
$currentline += $ps;
}
$str = implode($chars);
return $str;
}
function resolve($html){
file_put_contents("data.log", "NEW DATA : \n$html\n----------------------------\n", FILE_APPEND);
$data = explode("'", $html);
$data = explode(",",$data[1]);
$data = $data[1];
$image = new Imagick();
$image->readImageBlob(base64_decode($data));
$reponse = decode($image);
return $reponse;
}
$ns = "problems1.2016q1.sctf.io";
$host = gethostbyname($ns);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
echo "Connection à $ns\n";
socket_connect($socket, $host, 50000);
while(true){
echo "Reception\n";
$data = "";
while($out = socket_read($socket, 2048 )){
$data .= $out;
if(substr($out,-6) == "</img>")
break;
}
$in = resolve($data);
echo "Sending an answer\n";
echo "Answer : $in\n";
socket_write($socket, $in, strlen($in));
}
echo "The END\n";
socket_close($socket);
And eventually, after 200 resolutions, I get the flag from the server (in my log file) : flag: sctf{y0ub34tth3v3rt1c0d3}
The blog of an IT and security enthusiast.