// Ruby für Pentester · Teil 1

Dein erstes Recon-Werkzeug

Installiere Ruby 4, lerne irb kennen und schreib dein allererstes Skript — einen HTTP-Client, der ein Ziel fingerabdruckt. Wir richten ihn auf die pensionierte HackTheBox-Maschine Bizness und bestätigen, was läuft.

Rubynet/httpAufklärungEinsteiger
7 Min. Lesezeit

Willkommen. In dieser Reihe lernst du Ruby so, wie du es als Pentester wirklich einsetzt: indem du dein eigenes Werkzeug schreibst und es auf ein echtes Ziel richtest. Dieses Ziel ist Bizness, eine pensionierte HackTheBox-Maschine. Am Ende des letzten Teils hast du ein einzelnes Ruby-Programm — und danach ein echtes Metasploit-Modul —, das die gesamte Kette von einem leeren Terminal bis zur root-Shell trägt.

Kein Ruby-Vorwissen nötig. Dieser erste Teil ist das Fundament: bring die Sprache zum Laufen, lern, wie ein Ruby-Programm aufgebaut ist, und schreib ein Skript, das HTTP spricht und uns sagt, was auf der Box läuft.

Alles hier richtet sich gegen eine pensionierte Lab-Maschine, zu der du eine Verbindung besitzt. Bau Werkzeuge, richte sie auf Dinge, die du berühren darfst.

Ruby 4 besorgen

Die Seite, die du gerade liest, läuft auf Ruby 4, also ziehen wir gleich. Prüf, was du hast:

$ ruby -v
ruby 4.0.6 (2025-12-25 revision ...) [x86_64-linux]

Wenn das eine 4.x-Version ausgibt, bist du startklar. Falls nicht, installiere es mit dem Versionsmanager deiner Plattform (rbenv install 4.0.6, mise use ruby@4.0.6 oder das Paket deiner Distribution). Das exakte Patch-Level spielt für diese Reihe keine Rolle; alles auf der 4.0-Linie passt.

irb: dein Notizblock

Ruby bringt eine interaktive Shell namens irb mit. Sie führt Code aus, während du ihn tippst, und ist damit der schnellste Weg, etwas auszuprobieren. Starte sie, rechne ein bisschen und bau dann einen String:

$ irb
irb(main):001> 40 + 2
=> 42
irb(main):002> "hello".upcase
=> "HELLO"

Zwei Dinge fallen auf. Erstens: alles ist ein Ausdruck40 + 2 und "hello".upcase geben beide einen Wert zurück (irb druckt ihn nach dem =>). Zweitens: "hello" ist ein Objekt, und .upcase ist eine Methode, die du darauf aufrufst. In Ruby weiß selbst ein einfacher String, wie man Dinge tut. Darauf wirst du dich ständig stützen.

Tipp exit, um irb zu verlassen.

Dein erstes Skript

irb ist für Experimente; echte Werkzeuge leben in Dateien. Öffne eine Datei namens recon.rb und beginn mit einer Zeile:

puts 'hello from Ruby'

puts („put string”) gibt sein Argument samt Zeilenumbruch aus. Führ die Datei aus:

$ ruby recon.rb
hello from Ruby

Das ist die ganze Schleife, die du für immer nutzt: in eine .rb-Datei schreiben, mit ruby ausführen. Wenn du sie oft direkt startest, füg als erste Zeile eine Shebang hinzu, damit die Shell weiß, wie sie auszuführen ist:

#!/usr/bin/env ruby
puts 'hello from Ruby'

Dann chmod +x recon.rb, und du kannst ./recon.rb ausführen.

Variablen und Strings

Eine Variable ist ein Name für einen Wert. Keine Typdeklarationen — du weist einfach zu:

target = 'bizness.htb'
puts "scanning #{target}"

Das #{...} innerhalb eines doppelt gequoteten Strings ist Interpolation: Ruby wertet den Ausdruck aus und lässt das Ergebnis in den String fallen. Das ist sauberer als Strings mit + zusammenzukleben, und du wirst es in jedem Payload nutzen, den du später baust.

HTTP sprechen

Aufklärung beginnt mit einer einfachen Frage: Was antwortet, wenn ich anklopfe? Rubys Standardbibliothek hat einen HTTP-Client eingebaut — keine Gems zu installieren. Zieh ihn mit require herein:

require 'net/http'
require 'uri'

target = URI('https://bizness.htb/')

http = Net::HTTP.new(target.host, target.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE   # lab box uses a self-signed cert

response = http.get('/')

puts "Status: #{response.code} #{response.message}"
puts "Server: #{response['server']}"

Zeile für Zeile:

  • require 'net/http' lädt den HTTP-Client; require 'uri' lädt den URL-Parser.
  • URI('https://bizness.htb/') macht aus dem String ein URL-Objekt, sodass wir es nach .host (bizness.htb) und .port (443) fragen können, statt Strings von Hand zu zerlegen.
  • Net::HTTP.new(host, port) erzeugt einen Client. use_ssl = true, weil es HTTPS ist. Lab-Boxen liefern selbstsignierte Zertifikate aus, also sagt verify_mode = VERIFY_NONE Ruby, die Verbindung nicht abzulehnen — das reale Gegenstück zu curl -k.
  • http.get('/') schickt die Anfrage und reicht ein Response-Objekt zurück.
  • response.code, .message und response['server'] lesen Status und einen Header von diesem Objekt ab.

Führ es aus:

$ ruby recon.rb
Status: 200 OK
Server: nginx/1.18.0 (Ubuntu)

Ein nginx-Reverse-Proxy — aber das ist nur die Vordertür. Das Interessante ist, was dahinter steckt.

Wenn das Klopfen unbeantwortet bleibt

Ein echtes Ziel ist nicht immer erreichbar. Ist der Host down, der Port gefiltert oder das VPN abgebrochen, gibt http.get keine Response zurück — es wirft eine Exception, und dein Skript stirbt mit einem Backtrace. Ein Recon-Werkzeug sollte das erwarten und stattdessen etwas Nützliches sagen. Pack den riskanten Aufruf in begin/rescue:

begin
  response = http.get('/')
rescue StandardError => e
  abort "[-] #{target.host} is not answering: #{e.message}"
end
  • begin ... rescue ... end führt den Code aus und fängt jeden darin geworfenen Fehler.
  • StandardError ist der Elternteil der alltäglichen Fehler — eine abgelehnte Verbindung, ein Timeout, ein DNS-Fehlschlag —, sodass ein rescue sie abdeckt; => e bindet das Exception-Objekt, damit wir e.message lesen können.
  • abort gibt auf stderr aus und beendet mit Nicht-Null, so wie ein Kommandozeilen-Werkzeug scheitern sollte.

Exceptions sind Rubys Fehlerkanal. Du wirst sie rescue-n, wann immer du das Netzwerk, das Dateisystem oder irgendetwas anfasst, das unter dir wegbrechen kann — was in diesem Metier die meisten Dinge sind.

Die App fingerabdrucken

Der Response-Body ist das HTML, das der Server geschickt hat. Suchen wir nach einem Hinweis. Ergänze:

body = response.body

if body.include?('Apache OFBiz')
  puts '[+] Apache OFBiz detected'
else
  puts '[-] OFBiz not found in body'
end

response.body ist einfach ein String, und String#include? gibt true oder false zurück — einen Boolean —, je nachdem, ob der Teilstring vorhanden ist. Den füttern wir an ein if/else, um zu verzweigen. Lauf es erneut:

$ ruby recon.rb
Status: 200 OK
Server: nginx/1.18.0 (Ubuntu)
[+] Apache OFBiz detected

Da ist es. Apache OFBiz — eine große Java-ERP-Suite — hinter nginx. Diese eine Tatsache entscheidet die ganze Engagement: OFBiz hat eine bekannte Authentifizierungs-Bypass-CVE aus 2023, und den Stack zu bestätigen ist das, was uns dorthin weist. Du hast gerade das Werkzeug geschrieben, das ihn fand.

Das fertige Skript

Zusammengesetzt, dein erstes Recon-Werkzeug:

#!/usr/bin/env ruby
require 'net/http'
require 'uri'

target = URI('https://bizness.htb/')

http = Net::HTTP.new(target.host, target.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

response = http.get('/')
puts "Status: #{response.code} #{response.message}"
puts "Server: #{response['server']}"

if response.body.include?('Apache OFBiz')
  puts '[+] Apache OFBiz detected'
else
  puts '[-] OFBiz not found in body'
end

Zwanzig Zeilen, keine Abhängigkeiten, und es tut etwas Echtes.

Erkenntnisse

  • Ruby-Code lebt in .rb-Dateien, die du mit ruby ausführst, oder du experimentierst live in irb. Alles ist ein Objekt, und du rufst Methoden auf Objekten auf.
  • puts gibt aus; Variablen sind nur Namen, die du zuweist; die "#{...}"-Interpolation baut Strings sauber zusammen.
  • net/http + uri geben dir einen vollständigen HTTP-Client in der Standardbibliothek — Net::HTTP.new, use_ssl, VERIFY_NONE für Lab-Certs, .get, dann .code / .body vom Response ablesen.
  • Aufklärung ist nur, eine Frage zu stellen und die Antwort zu lesen. Unsere fand Apache OFBiz, und das ist der Faden, an dem wir als Nächstes ziehen.

Übungen

Ein Werkzeug zu lesen ist nicht, eines zu schreiben. Probier diese, bevor du weitergehst:

  1. Gib den Content-Type-Header der Response und die Länge des Bodys in einer Zeile per Interpolation aus — z. B. text/html; charset=utf-8, 24187 bytes.
  2. Füge ein elsif hinzu: Erwähnt der Body kein OFBiz, enthält aber der Server-Header nginx, melde [?] nginx, unknown app. Fall nur in den [-]-Zweig, wenn keines passt.
  3. Richte das Skript auf einen Port, auf dem nichts lauscht (URI('https://bizness.htb:9999/')), und bestätige, dass dein begin/rescue eine saubere Zeile statt eines Backtraces ausgibt.

Weiter — Teil 2: Wir verwandeln diesen OFBiz-Fund in Zugang und schreiben eine Ruby-Funktion, die direkt durch die Authentifizierungsprüfung marschiert.

← Alle Teile