// Ruby für Pentester · Teil 7

Root werden

Das geknackte Passwort ist auch das von root — schlichte Passwort-Wiederverwendung. Wir setzen es zum Eskalieren ein und lernen die wenigen Wege, wie Ruby externe Programme ausführt — system, Backticks, Open3 — und enden mit einem PTY, um ein interaktives su zu steuern.

RubyPrivilege EscalationOpen3PTY
4 Min. Lesezeit

Teil 6 hat uns monkeybizness gereicht — das Passwort des OFBiz-Admins. Hier die Belohnung: Auf dieser Box ist das auch das Login-Passwort von root. Passwort-Wiederverwendung ist die häufigste Rechteausweitung überhaupt, und sie ist der Grund, warum das Knacken eines Hashes so oft das ganze Engagement beendet. Es bleibt nur su. Wir tun es aus Ruby, was heißt, zu lernen, wie Ruby andere Programme ausführt — ein Werkzeugkasten, den du in jedem Skript nutzt.

Auf einer Box ohne Ruby tippst du einfach su und das Passwort von Hand. Es geht hier um Rubys Werkzeugkasten zum Starten von Programmen; die Eskalation ist die Demo.

Drei Wege, ein Kommando auszuführen

Ruby gibt dir eine Leiter von Optionen, von grob bis präzise:

system('id')          # run it; output goes to the screen; returns true/false
output = `id`          # backticks: capture stdout as a string
output = %x[id]        # exactly the same as backticks, different delimiters

require 'open3'
stdout, stderr, status = Open3.capture3('id')
puts stdout if status.success?
  • system führt das Kommando aus, lässt seine Ausgabe auf dein Terminal fließen und gibt true/false für Erfolg zurück — gut, wenn dich nur interessiert, ob es geklappt hat.
  • Backticks (und %x[], dasselbe) führen das Kommando aus und geben seinen stdout als String zurück — gut, wenn du die Ausgabe willst.
  • Open3.capture3 ist der präzise: Es gibt stdout, stderr und den Exit-Status getrennt zurück, sodass du echte Ausgabe von Fehler-Rauschen trennen kannst. Greif danach, wann immer Fehler zählen.

Das ist das ganze Spektrum. Aber keiner davon kann uns eskalieren — und es lohnt sich zu verstehen, warum.

Warum su anders ist

Man würde denken, echo monkeybizness | su funktioniere. Tut es nicht. su liest sein Passwort direkt vom Terminal — dem TTY —, nicht von der Standardeingabe, mit Absicht, damit Passwörter nicht aus einem Skript hineingepiped werden können. Backticks und Open3 geben dem Kind eine Pipe, kein Terminal, also verweigert su sie.

Die Lösung ist, ihm ein Terminal zu geben — ein falsches, das wir steuern.

Ein Pseudo-Terminal mit PTY

Rubys pty-Bibliothek allokiert ein Pseudo-Terminal, und sein expect-Begleiter lässt uns auf Prompts warten und sie beantworten — steuert su genau so, wie ein Mensch es täte:

require 'pty'
require 'expect'

PTY.spawn('su - root') do |reader, writer, _pid|
  writer.sync = true
  reader.expect(/Password:/, 5)
  writer.puts 'monkeybizness'
  reader.expect(/[#$] /, 5)
  writer.puts 'id'
  puts reader.expect(/uid=\d+.*/, 5)&.first
  writer.puts 'exit'
rescue Errno::EIO
  # the child shell closed — normal once we exit
end
  • PTY.spawn('su - root') startet su unter einem Pseudo-Terminal und liefert ein reader/writer-Paar (plus die Kind-pid) — die beiden Enden dieses Terminals.
  • reader.expect(/Password:/, 5) blockiert, bis su seinen Prompt druckt, und gibt nach 5 Sekunden auf (liefert nil bei Timeout).
  • writer.puts 'monkeybizness' tippt das Passwort; wir warten auf den #-Prompt der Root-Shell, führen id aus und lesen die Antwort zurück.
  • Wenn die Shell endet, wirft das Pseudo-Terminal Errno::EIO — die normale Art, wie ein PTY das Ende des Kindes signalisiert, also fangen wir es.

Lauf es:

$ ruby root.rb
uid=0(root) gid=0(root) groups=0(root)

uid=0(root) — root, ganz aus Ruby erreicht, auf die Kraft eines einzigen wiederverwendeten Passworts. Das ist die Box.

Erkenntnisse

  • Ruby führt externe Programme auf drei Wegen aus: system (Status, Ausgabe auf den Bildschirm), Backticks/%x[] (stdout einfangen) und Open3.capture3 (stdout, stderr und Status getrennt — die präzise Wahl).
  • Manche Programme, su darunter, verlangen ein echtes Terminal und ignorieren gepipte Eingabe — eine Pipe ist kein TTY.
  • PTY.spawn allokiert ein Pseudo-Terminal und expect steuert es: auf einen Prompt warten, eine Antwort schreiben. Es wirft Errno::EIO, wenn das Kind endet.
  • Passwort-Wiederverwendung ist eine erstklassige Rechteausweitung: ein geknacktes Zugangsdatum ist es wert, gegen jedes Konto und jeden Dienst der Box probiert zu werden.

Übungen

  1. Schreib die id-Prüfung mit Open3.capture3 um und gib stderr nur aus, wenn der Exit-status nicht erfolgreich ist.
  2. Pack die Eskalation in ein Prädikat root?, das zurückgibt, ob die PTY-Sitzung uid=0 erreicht hat, und fang einen Falsch-Passwort-Timeout als false ab.
  3. Verallgemeinere den PTY-Block zu as_root(cmd), das eskaliert, ein Kommando als root ausführt und dessen Ausgabe zurückgibt — dein eigener kleiner su-Runner.

Weiter — Teil 8: Acht Skripte, eine Form. Wir falten Recon, Bypass, RCE und Loot in eine wiederverwendbare Klasse mit einer echten Kommandozeilen-Schnittstelle.

← Alle Teile