Hacking (CTF)/웹 해킹

[Dreamhack/드림핵] simple_sqli

gapsoo 2023. 10. 6. 22:17

 

 

https://dreamhack.io/wargame/challenges/24

 

simple_sqli

로그인 서비스입니다. SQL INJECTION 취약점을 통해 플래그를 획득하세요. 플래그는 flag.txt, FLAG 변수에 있습니다. Reference Server-side Basic

dreamhack.io

 

 

 


문제 첫 시작 화면이다.

 

위쪽에 HOME, ABbout, Contact 메뉴가 있고 (초기 화면 = HOME 클릭 시 나오는 페이지 화면)

 

 

 

About, Contact를 누르면 위와 같은 페이지로 이동하는데

 

로그인을 안 한 상태여서 그런가? 주소만 바뀌고 HOME과 같은 화면이 뜬다.

 

 

 

 

 

메인 화면에는 아이디와 패스워드를 입력하는 화면으로 넘어가는 Login 버튼이 있다.

 

오른쪽 위에 있는 Login 버튼도 같은 페이지로 넘어가는 기능을 한다.

 

 

 

아이디, 패스워드 모두 'abc'를 입력해 로그인을 시도해 보았다.

 

 

"wrong"이라는 메시지가 뜨고 로그인이 되지 않는다.

 

 

 

 

로그인 페이지의 소스코드를 확인해 보자.

 

음...

 

SQL injection 공격을 통해 문제를 풀려면 

 

공격 구문에 대해 알아야 할 것 같아서 검색을 해보았다.

 

참고한 블로그: https://blog.naver.com/isc0304/220594576257

 

[발췌] SQL 인젝션에 사용되는 해킹 구문

해킹 사고의 재구성 작가 최상용 출판 에이콘출판 발매 2012.08.29. 1. 기본  1.1. http://xxx.xxx.xx...

blog.naver.com

 

위의 블로그의 글 중에 

 

로그인 인증 우회
   1) ' or''='
   2) ' or 1=1--
   3) ' or 'a'='a--
   4) 'or''='or'
   5) " or 1=1--
   6) or 1=1--
   7) or 'a='a
   8) " or "a"="a
   9) ') or ('a'='a
  10) ") or ("a"="a
  11) ) or (1=1

[출처] [발췌] SQL 인젝션에 사용되는 해킹 구문|작성자 일선스

 

이 부분을 참고해서

 

5, 8, 10을 아이디와 패스워드에 입력해보았다.

(로그인 페이지 소스코드를 보니 큰 따옴표가 사용되었길래... 큰 따옴표가 포함된 공격 구문을 입력해보았음)

 

 

그랬더니 이런 메시지가 떴다.

 

 

 

계정 정보 변경
   ;exec sp_addlogin '아이디', '패스워드';--
   ;exec sp_password '이전 패스워드', '새로운 패스워드', '아이디';--
   ;exec master.dbo.sp_addsrvrolemember '아이디' 'sysadmin';--
   ;exec sp_droplogin '아이디';--
[출처] [발췌] SQL 인젝션에 사용되는 해킹 구문|작성자 일선스

 

이번엔 이 부분의 첫 번째 구문을 입력해보았다.

 

 

 

그랬더니 이런 에러 화면이 나타났다.

 

 

 

 

아이디: "
비밀번호: "이 아닌 아무거나

 

이렇게 입력하면 

 

 

 또 이런 에러 화면이 나오는 것으로 보아

 

큰 따옴표가 문제인 것 같다.

 

 

 

 

 

SQL 인젝션 공격 기법이라고 검색하면,

 

WHERE이나 SELECT가 포함된 소스 코드를 확인하고 공격하던데

 

소스코드 어디에서도 WHERE이나 SELECT를 찾을 수 없었다.

 

 

 

 

 

 

 

문제 서버에 들어가기 전에 문제 파일을 받을 수 있는데

 

이게 생각나서 이제야 파일을 받아서 확인해보았다.

 

 

 

app.py라는 파일을 다운로드할 수 있었다.

 

아까 에러 화면에서 "there is an error in the application."이라고 했는데

 

app.py 파일을 찾을 수 있어서 문제 풀이에 도움을 많이 받을 것이라고 생각했다. 

 

"select * from...... where... "이 포함된 이 부분이 문제 풀이에 도움이 될 부분 같아서 캡쳐해왔다.

 

 

 

 

 

다시 웹페이지의 로그인 페이지의 소스코드랑 app.py를 동시에 확인하면서 해석을 시도해보겠다.

 

 

 

로그인 웹페이지의 소스코드
app.py

 

 

 

내가 해석하려고 노력한 결과... (틀릴 수 있음)

 

 

1. 로그인 웹페이지에서 입력받은 아이디와 패스워드가 각각 'userid'와 'userpassword'로 받아지고
2. 그 값이 애플리케이션에 반영이 되는 것 같다.
3. admin이라는 아이디로 로그인하면 flag 값이 나오는 것 같다. 그러므로 아이디 값은 이제부터 admin으로 계속 입력하면서 비밀번호를 바꿔서 시도해 보면 될 듯하다.
4. 큰따옴표를 사용해서 비밀번호 입력에 sql injection 공격을 하면 될 것 같다.

 

 

 

그럼 이제 아이디에는 계속 admin을 입력하고 

 

비밀번호에 큰따옴표를 사용하여 공격문을 입력해보겠다.

 

 

# 첫 번 째 시도

아이디: admin
비밀번호: "or 1=1#

 

 

위와 같이 입력하면

 

select * from users where userid="{admin}" and userpassword="{"or 1=1#}"

 

이러한 쿼리가 실행되는데,

 

맨 처음 "를 사용해 앞에서 열어준 큰따옴표를 닫아주고

 

or 1=1 은 참이 되고

 

#을 삽입해주어 #이후의 문장은 주석 처리가 되게 한다.

 

 

그러면 아래와 같은 쿼리가 된다.

 

select * from users where userid="{admin}" and true;

 

 

따라서 DB는 users 테이블에 저장된 id가 admin인 사용자의 정보를 출력한다.

 

쿼리의 실행 결과가 존재하기만 하면 로그인에 성공하기 때문에 admin으로 로그인에 성공하게된다.

 

 

 

참고: https://velog.io/@910/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%94%B0%EB%A5%B8-SQLi-%EC%9D%B8%EC%A6%9D-%EC%9A%B0%ED%9A%8C

 

 

 

그러나 먹히지 않았다.

 

음... 뭐가 문제여서 되지 않은 걸까?

 

내가 입력한 비밀번호와 DB에서 가져온 비밀번호가 일치하지 않아서인가...

 

근데 그러면 wrong이나 hello guest가 떠야 하는 거 아닌가...?

 

wrong이 뜨는 경우와 hello guest가 뜨는 경우의 차이점은 뭐지...

 

혼란 속에 빠졌다...

 

 

 

 

app.py에 있는 DB 관련 정보를 확인해봤다.

 

userpassword에 해당하는 

 

"{binascii.hexlify(os.urandom(16)).decode("utf8")}" 부분이 무슨 뜻인지 몰라서 챗GPT에게 물어봤다.

 

binascii.hexlify(os.urandom(16)).decode("utf8") 는 Python에서 사용되는 코드입니다.

이 코드는 랜덤한 16바이트의 바이너리 데이터를 생성하고, 그 데이터를 16진수로 변환한 후 UTF-8 형식으로 디코딩합니다.

여기서 각 부분을 하나씩 살펴보겠습니다:

1. os.urandom(16): os 모듈에서 urandom 함수를 호출하여 16바이트의 무작위 데이터를 생성합니다. 이 데이터는 암호학적으로 안전한 난수입니다.
2. binascii.hexlify(...): binascii 모듈에서 제공하는 hexlify 함수를 사용하여 바이너리 데이터를 16진수로 변환합니다.
3. .decode("utf8"): 이 변환된 16진수 데이터를 UTF-8 문자열로 디코딩합니다.


최종적으로, 이 코드는 무작위의 16바이트 데이터를 16진수 문자열로 변환하고, 그 문자열을 UTF-8로 디코딩하여 출력합니다. 이런 식으로 생성된 값은 주로 무작위 토큰이나 비밀 키를 생성할 때 사용됩니다.

 

패스워드가 난수 값(random)이므로 서비스 이용자인 우리는 알 수 없다.

 

여기를 들여다보는 건 의미 없는 것 같아서 다른 방법을 찾아봤다.

 

위에서 시도했던 방법은 SQL 쿼리 한 줄에 식별과 인증을 동시에 처리하는 방식이고

 

이제 식별과 인증을 분리하여 처리하는 방식(UNION BASED)을 사용할까 했는데,

 

기존의 쿼리에서 출력하는 Column의 개수를 알고 일치시켜 활용하는 공격 기법이므로

 

Column의 개수를 몰라서 이것도 포기...

 

여기서부터 문제 풀이 정리글을 찾아봤다.


 

 

구글링 결과 SQL 문법에서는 주석 처리 문법이 -- 라고 한다.

 

이를 이용해 통해 ID 검색 조건만을 처리하도록 두고

뒤의 userpassword 조회 부분은 주석처리해버리면 참값이 된다고 한다.

 

 

참고: https://hobbylists.tistory.com/entry/%EB%93%9C%EB%A6%BC%ED%95%B5-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Simplesqli-SQL-Injection-query-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%9A%B0%ED%9A%8C-SQL

 

드림핵 문제풀이 - Simple_sqli // SQL Injection, query, 로그인우회, SQL

문제 분류 난이도 : 하하 1. 사전 탐색&정찰 (reconnaissance) 사이트에 접속해보면 Login이 보입니다. 그리고 코드를 함께 살펴봅시다. #!/usr/bin/python3 from flask import Flask, request, render_template, g import sqlite3

hobbylists.tistory.com

 

 

admin뒤에 "-- 를 붙여서 아이디 값으로 넣어주고

 

비밀번호는 아무거나 입력한다.

 

 

 

 

 

 

 

정답!

 

 

SQL 문법에서 -- 를 사용하여 주석 처리한다는 사실만 알면 쉽게 풀 수 있는 문제였던 것 같다.