CVE-2024-3400: PAN-OS Command Injection Vulnerability in GlobalProtect Gateway. Learn More

CVE-2024-3400: PAN-OS Command Injection Vulnerability in GlobalProtect Gateway. Learn More

Services
Capture
Managed Detection & Response

Eliminate active threats with 24/7 threat detection, investigation, and response.

twi-managed-portal-color
Co-Managed SOC (SIEM)

Maximize your SIEM investment, stop alert fatigue, and enhance your team with hybrid security operations support.

twi-briefcase-color-svg
Advisory & Diagnostics

Advance your cybersecurity program and get expert guidance where you need it most.

tw-laptop-data
Penetration Testing

Test your physical locations and IT infrastructure to shore up weaknesses before exploitation.

twi-database-color-svg
Database Security

Prevent unauthorized access and exceed compliance requirements.

twi-email-color-svg
Email Security

Stop email threats others miss and secure your organization against the #1 ransomware attack vector.

tw-officer
Digital Forensics & Incident Response

Prepare for the inevitable with 24/7 global breach response in-region and available on-site.

tw-network
Firewall & Technology Management

Mitigate risk of a cyberattack with 24/7 incident and health monitoring and the latest threat intelligence.

Solutions
BY TOPIC
Offensive Security
Solutions to maximize your security ROI
Microsoft Exchange Server Attacks
Stay protected against emerging threats
Rapidly Secure New Environments
Security for rapid response situations
Securing the Cloud
Safely navigate and stay protected
Securing the IoT Landscape
Test, monitor and secure network objects
Why Trustwave
About Us
Awards and Accolades
Trustwave SpiderLabs Team
Trustwave Fusion Security Operations Platform
Trustwave Security Colony
Partners
Technology Alliance Partners
Key alliances who align and support our ecosystem of security offerings
Trustwave PartnerOne Program
Join forces with Trustwave to protect against the most advance cybersecurity threats
SpiderLabs Blog

Hunting For Integer Overflows In Web Servers

Allow me to set the scene and start proceedings off with a definition of an integer overflow, according to Wikipedia:

“…an integer overflow occurs when an arithmetic operation attempts to create a numeric value that is outside of the range that can be represented with a given number of digits – either higher than the maximum or lower than the minimum representable value.” [1]

To be inclusive of all audiences here, in software security we’ve got sources (typically user input) and sinks – where that input (the data) ends up. In order to overflow something (e.g. an integer overflow) we clearly need some way to be able to do that (think pouring water from a kettle into a cup), and that’s the source (us using the kettle) to overflow the cup. Cup of tea aside, what things can be accessed remotely and take user input (those sources)?

Web servers! This blog post title does not lie!

For us to start hunting these integer overflows down, we first need to look at two case studies from the past to both inspire us, let us know where we should maybe start looking and to get an appreciation for why appsec is not easy. Before we do any hacking of our own, let’s look at these two handpicked examples, let’s get into that hacker hoody mindset.

Let me take you on a journey, back to 2011, 24th August 2011 to be exact.

 

CVE-2011-3192, aka “Range header DoS vulnerability Apache HTTPD prior to 2.2.20”

“A denial of service vulnerability has been found in the way the multiple overlapping ranges are handled by the Apache HTTPD server prior to version 2.2.20... The attack can be done remotely and with a modest number of requests can cause very significant memory and CPU usage on the server.” [2]

The “Range” header (and later, also the “Request-Range” header) could be abused to cause this DoS. What is this range header I hear you ask?

“The Range HTTP request header indicates the parts of a resource that the server should return. Several parts can be requested at the same time in one Range header, and the server may send back these ranges in a multipart document… Currently, only bytes units are registered which are offsets (zero-indexed & inclusive).” [3]

For example:

  • To request the first 100 bytes: Range: bytes=0-99
  • The second 100 bytes: Range: bytes=100-199
  • The remaining 100 bytes of a 600-length resource: Range: bytes=500-
  • If the length of the resource is unknown, the last n bytes can be retrieved using a suffix range of -n: Range: bytes=-100

Now what went down was a request with lots of byte ranges, tons and tons, all with crazy offsets, mixed and matched up, conflicting with each other, overlapping, you name it… ultimately causing Apache (and in turn, the CPU usage of it) to go a little crazy server side.

Example: Range: bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,5-10,5-11,5-12,5-13 […and so on]

People were fixing this at the time with temporary patches using mod_rewrite and mod_headers, limiting the ranges there or unsetting the Range and Request-Range directives altogether.

This bug was then fixed in the codebase itself (modules/http/byterange_filter.c) the next day on 25th August 2011 and the following days with further amendments to close out edge cases as and when people were bypassing I imagine.

Code fix: Make maximum ranges to be limited to “200”.

Code fix Make maximum ranges to be limited to “200”.

 

Code fix: Check that the sum of all the lengths is not greater than the actual content length.

Code fix Check that the sum of all the lengths is not greater than the actual content length.

 

Code fix: Stop ranges values less than “1” being submitted.

Code fix stop ranges values less than 1 being submitted

 

Code fix: Check that the start of a range is not less than the end of it, both singular and in multiple ranges (the merging of ranges looks to be painful).Code fix 4

 

That was the first of two overflows we will be looking at in this blog post. For the second, let me change the dial on that DeLorean to 6 years later, 2017 – 11th July 2017 to be exact. 88mph and 1.21 Gigawatts later...

 

CVE 2017-7529, aka “Nginx: Integer overflow in the range filter”

“Nginx versions since 0.5.6 up to and including 1.13.2 are vulnerable to integer overflow vulnerability in nginx range filter module resulting into leak of potentially sensitive information triggered by specially crafted request.” [4]

Would you look at that, it is an overflow in a range filter again… but this time in Nginx - vulnerable for 6 years, a variant of the Apache vulnerability from back in 2011. In this one the impact was limited, it would allow reading (leaking) of a cache file header (if the file was being served from a cache), which may reveal the internal IP address of the backend server or other sensitive information. To exploit, you had to do a few more things, first get the content-length of the file being returned, you’d then add 623 to this. This would be your first minus/negative range (or ‘suffix’). You’d then take this number and subtract it from 776000, the result of which you append onto the end of the next suffix; -9223372036854. I told you there was a little more involved!

Example:

Content-length received is 42.

42 + 623 = 665

776000 – 665 = 775335

Range: bytes=-665,-9223372036854775335

 

They fixed this (src/http/modules/ngx_http_range_filter_module.c), making sure that when any suffix is provided (the “-“ symbol), that the ‘end’ is less than the content-length available, and dealing with this if not.Code fix 5

 

Okay. So now that those two case studies are firmly fresh in our minds, what did we learn? Well firstly, that the first rule of software security club is still true; never trust user inputs, that’s a given. Secondly, that the developers (in both these examples) are allowing the user to specify offsets/ranges, a sliding window if you like, of the data they want to receive from the server. The problem here is that checks (or sufficient ones) were not put in place to control the start and end positions, so we get to slide our window out of those ranges and place it into other memory regions. There is added complexity around being able to specify multiple ranges which then need to be merged, which created all sorts of issues as we saw in the first DoS example. The second example was abusing the ‘suffix’ (again with multiple ranges) to move that sliding window. In the second example the result was a leak, we’re not able to write data here and overwrite EIP like in a buffer overflow, but the concept is similar. However, we can abuse the logic, overflow these ranges/offsets to control a pointer and put it places it shouldn’t be.

So this got me thinking, in what other thing, similar to the range theme, can I specify a ‘start’ or ‘end’ position to do with web servers, web technology, etc. which I can potentially abuse?

Videos!

We can skip to different positions in a video by using a slider – start from 5 seconds in, etc. Video multimedia on the web over the years have been incorporated into web server technology, especially when streaming is involved. With Nginx in the picture, I did some digging, and decided to target Flash Video (FLV) files. Nginx has a module called “ngx_http_flv_module” and wait, you need to be sitting down when you read this, move that coffee cup away…

“The ngx_http_flv_module module provides pseudo-streaming server-side support for Flash Video (FLV) files… It handles requests with the start argument in the request URI’s query string specially, by sending back the contents of a file starting from the requested byte offset and with the prepended FLV header.” [5]

Well, it is like all my Christmases came at once here – “start argument in the request” and “sending back the contents of a file starting from the requested byte offset”. I mean, that’s pretty much an open invite.

Let’s download the latest Nginx source code and have a little look. In src/http/modules/ngx_http_flv_module.c we go.

Nginx source code

 

The line I want to draw your attention to is line 174. If ‘start’ is equal to NGX_ERROR or ‘start’ is greater than or equal to ‘len’ (the length of the data), then ‘start’ is set to a value “0”. I want to also draw your attention to what we don’t see here. What happens if ‘start’ is less than ‘len’, and by less, I mean not just less (as intended), but much less, less like a negative value? That black hacker hoody is now firmly on.

Well, there is nothing more left for it. We have talked the talk; it is now time to see if we can walk the walk. We need to build this bad boy and see what we’re able to do.

Since the FLV module is not built by default in Nginx we need to specify it to be built. We also want to enable debugging so we can tail -f those log files and see what is going on in deep deep Nginx land:

./configure --with-debug –with-http_flv_module

We also need to add in the FLV directive into nginx.conf so it knows what to do with it:

location ~ .flv$ { flv; }

I fire Nginx up and also launch Burp Suite because we’re going to be using it to cook up some HTTP pot pies.

Not so fast though… I’m going to need a test FLV to play with. I make a screen recording of doing a directory listing – just something random to fill up 10 seconds or so to give us something to play with. This is in .MOV format so I use ffmpeg to convert this to .FLV. I then drop this file (test.flv) into the web root of Nginx. We are now all set.

Let’s see what a normal request/response for test.flv gives us back to get some kind of baseline before we start trying to hack all the things. Note down the content-length of 1829479.

normal request response for test.flv

 

We now introduce the “start” parameter, but don’t give it any value to see what happens in reality. The same content-length of 1829479 is returned.

test.flv 2

 

A value of zero? Content-length is still 1829479.

value of zero

 

A start value of “25”. Notice how (in the response) after the “FLV” header (mentioned shortly) we no longer have that superscript underlined “o”. We are effectively sliding our window over like we did with the range fun and games earlier. Our start value is being used as an offset to start from “25”. The content-length is less than the previous one (it is now “1829467” vs “1829479”). This is as we’d expect as we are chopping some bytes off, disregarding bytes from the start.

start value of 25

 

A start value of “30”. Notice how (in the response) after the FLV header, that the file starts with “etaDataduration” which was previously “onMetaDataduration”. Proof we are moving that window thing and disregarding bytes before it, as you’d expect from changing an offset. Content-length decreases further in line with this.

start value of 30

 

At this stage, you may be wondering what I’m up to. I’m matching up what I read about in the source code and seeing if the actions I expect to happen do in reality go down that way. A lot of the time we can’t see everything from the source code review and it isn’t always representative about all of the other layers which may happen or get invoked during execution. For example, the “FLV” header we saw in the code:

FLV header

 

We clearly knew to expect this header in the response as shown below. If you look to the right in Burp Suite below and see the hexadecimal values of this header, you can see how this maps to the ngx_flv_header[] from the code above on line 27.

ngx_flv_header from the code above on line 27

 

Ok, back to it.

A really big number? It gets handled correctly, just as we expected from the source code – the start gets set to “0” and everything gets sent – which the content-length confirms, back to “1829479”.

A really big number

 

A negative number? Which is something we were really interested in right from the outset because it wasn’t clear in the code how this would be handled. Well, it looks like this exception gets caught and handled nicely because it sends the full length file, treating ‘start’ effectively as if it were “0”.

A negative number

 

Ok, what about a resultant negative value? Not by sending a negative value but overflowing by 1 byte and hoping this overflows with some kind of wraparound and turns into a negative number. We know that the full content-length is “1829479”, so let’s send a ‘start’ value of “1829480” to try just that. resultant negative value

 

The web server gives us back an empty response. We’re onto something, maybe… We need to now go look at the Nginx debug logs to find out what is happening internally.

Nginx debug logs

 

Unfortunately, it looks like the exception has been spotted and caught. If we look at the “[alert]” above it mentions “negative size buf in output” and in the line below it there is the calculation “1829480-1829479” (which I am sure is being reported the wrong way around for logging purposes else this would be “1”). The resultant value is actually “-1”, hence the negative alert trigger.

What is responsible for catching this? If we search for the string “negative size buf in output” we get a hit in src/core/ngx_output_chain.c .

negative size buf in output

 

If we take a look at the code we can immediately see the ‘if’ conditional block screaming out at us, checking that if ‘bsize’ (the byte size of the content to be returned) is less than “0” then log the error (as an alert – the one which we saw) and call ngx_debug_point() which actually causes a break inside that function – hence the empty response we got back from the web server.

bsize

 

So we lost the hunt on this one, we won’t return back home with any prize. However, don’t be disheartened, I’m certainly not! That’s because there are many more web servers and many more input sources out there in HTTP itself, associated web technology and stacks, just waiting to be discovered and overflowed.

Happy hunting, and thanks for reading!

 

References

[1] https://en.wikipedia.org/wiki/Integer_overflow

[2] https://httpd.apache.org/security/CVE-2011-3192.txt

[3] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range

[4] https://mailman.nginx.org/pipermail/nginx-announce/2017/000200.html

[5] https://nginx.org/en/docs/http/ngx_http_flv_module.html

Latest SpiderLabs Blogs

EDR – The Multi-Tool of Security Defenses

This is Part 8 in my ongoing project to cover 30 cybersecurity topics in 30 weekly blog posts. The full series can be found here.

Read More

The Invisible Battleground: Essentials of EASM

Know your enemy – inside and out. External Attack Surface Management tools are an effective way to understand externally facing threats and help plan cyber defenses accordingly. Let’s discuss what...

Read More

Fake Dialog Boxes to Make Malware More Convincing

Let’s explore how SpiderLabs created and incorporated user prompts, specifically Windows dialog boxes into its malware loader to make it more convincing to phishing targets during a Red Team...

Read More