16번째로 푼 문제이다.! 
바로 blind sql injection! 음 정확히 말하면 블라인드?라고는 아무튼 인젝션 문제이다!

ip log table!


(ip log table)



문제 진입 전에 설명에 Ascii를 Date로 사용할 수 있다는 문구가 있다.
나는 오래 헤맨 뒤에야 이 문구의 의미를 깨달을 수있었다.



(힌트)



문제 페이지


(문제 페이지)



문제 페이지는 위와 같다.
아이피 가보이고 클릭해보면 해당 아이피의 접속 시간? 시간정보가 나온다.

소스코드를 열어서 확인해보니 hidden 값으로 몰래 값이 전달되고있었다.



(form)



idx에 값을 넣어 쿼리가 전달될 것이라고 생각해볼 수 있다.

즉 이 form문을 이용해 쿼리를 보낼 것이다.



(사용할 form문)



(사용할 form문 개조)



이 form을 이용하여 내 예상이지만 where idx=$_POST['idx']
요론 식으로 되어있지 않을까 예상이 된다.
idx니까 numeric이 겠지? 라는 기대를 가지고 1 or 1을 날려본다.



(1 or 1 injection)





(실행결과)



실행결과가 나온다.
사실 이 정보만으로 numeric이야 라고 섣불리 판단할 수 있지는 않다.
이유는 1 or 1 이렇게 전달했지만 웹 페이지 내에서 int만 parse 해서 사용하고 있을 수도 있고
그 외에 문자가 들어와서 디폴트로 줄수도 있고 다양한 변수들이 존재하지만

나의 수많은 쿼리들을 이 곳에 다 담을 수 없기에 정리한다 여기는 numeric으로 쿼터 없이 입력이 가능하다!

그 후 1 or 1 union select 77 limit 15,1 을 인젝션하다가 발견한 것이 있다.
여기서 limit 15,1 을 한 이유는 앞의 쿼리에서 14개의 행이 나오고 그 다음 내가 union 한 결과가 15행 부터 나오기에 15행 하나만 꺼내서 보기위해 그랬다.



(특이점 발견)





(특이점)



무심코 지나칠 뻔 했는데,
원래 없는 값은 1970-01-01 09:00:00 으로 나오다가
왠걸 조금 다른 값이 나온다.

불현듯 든 생각이 앞의 그 힌트였다. 혹시 데이터에 저장되고 있는것이 유닉스 타임 스탬프이고
그 값에 따라서 이 페이지에서 날짜 형태로 출력되는 걸까?
나는 유니온을 해서 77을 select 했다. 그 결과 01:17...
초로 따지면 77 !
다른 값으로 더 해보아 확인해보았다.

이제 정보를 알아낼 차례이다.!
나는 1 or 1 union select (숫자형태로 나오게 select 한글자) limit 15,1
요것을 이용하여 알아낼 것이다.

먼저 table 이름을 알아야한다.




(테이블 이름)





(테이블 이름 알아가는 중)



내가 알아낸 테이블 이름은 (기본적인거 빼고)
admin_table (누가봐도 수상하다.)
ip_    (여기까지 알아봤다. ip_table일거 같고 본 페이지에서 사용하는 테이블일 것이다.)
그 외의 테이블은 없었다.

문제 페이지에서 관리자로 로그인하는 페이지가 있었는데, 바로 관리자의 계정과 패스워드를 알아내라는 뜻인거 같다.

그렇다면 컬럼 이름을 알아내자!
컬럼이름을 쓰려면 ' ' 쿼터를 사용해야하는데, 확인은 안됬지만 왠지 쿼터정도는 막혀있을 예상이 들어 아싸라하게 char을 이용하여 컬럼이름을 검색해주었다.



(admin_table 다른 표현)



컬럼이름 알아내기 시작!



(컬럼이름 injection)





(찾아가는 중)



그 결과
id, ps
라는 2개의 컬럼만 있다는 사실을 알았다.

사실 이 정보는 관리자 로그인 페이지에서 form문에서 눈치 챌수 있었지만 확실히 하기 위해 찾아보았다.

그 다음은 id의 값을 알아낼 것이다!



(id injection)





(찾는 중)


찾아낸 아이디는 공개하지 않겠다! :)

그 다음은 비밀번호 찾기!



(비밀번호 injection)




(찾는 중)



비밀번호까지 찾았으니 관리자로 로그인 해본다!



(관리자로 로그인)





(문제 해결)



로그인 하니 바로 플래그가 나오고 문제를 해결할 수 있었다! :)




'WarGame > 500 Project' 카테고리의 다른 글

(18/500) Lord of the BOF - cobolt  (0) 2017.05.05
(17/500) Lord of the BOF - gremlin  (0) 2017.05.05
(15/500) Wargame.kr - lonely guys  (0) 2017.04.18
(14/500) Wargame.kr - strcmp  (0) 2017.04.16
(13/500) Wargame.kr - SimpleBoard  (0) 2017.04.16


오늘은
제로보드의 SQL 취약점을 살펴볼 것이다.

그 중에서 PHP File Download 취약점을 살펴 볼 것이다.

지난 글에서 만들었던 게시판에서 글을 확인해 보면
다운로드 옆에 숫자가 보이는데
다운로드 횟수를 보여준다.



(다운로드 횟수)



바로 이 횟수가 DB에 저장된 값 중 하나인데
이 페이지의 소스코드를 분석해보자

다운로드를 클릭하면 download.php 파일로 이동하는 것을 확인 할 수 있다.



(download.php 파일 이동)



download.php?id=gogo_test&page=1&page_num=20&category=&sn=off&ss=off&sc=off&keyword

=&prev_no=&select_arrange=headnum&desc=asc&no=19&filenum=1
코드를 보면 download.php 파일로 GET방식으로 변수들의 값을 가져가고 있는 것을 확인 할 수 있다.

그렇다면 download.php 파일을 확인해보자.



(download.php 파일)



소스 코드를 보면 44번 행에
mysql_query를 볼 수 있다.



(SQL 구문)



mysql_query("update $t_board"."_$id set douwnload".$filenum."=download".$filenum."+1 where no='$no'");
이 구문인데 실행될 떄 어떤 SQL이 만들어져서 보내지는지 확인해 보겠다.
먼저 $t_board 변수인데
download.php 페이지에서는 이 변수를 정의하고 있지 않다.
대신
이 페이지의 위 쪽에 코드를 보면
require "lib.php";  에서
즉 lib.php에서 이 변수를 정의하고 있는 것을 확인 할 수 있었다.



(lib.php 에서 정의)



확인한 결과
lib.php 39번라인에서 $t_board = "zetyx_board";로 정의 되어있었다.

또 나머지 변수는 GET 방식에서 넘어오는 변수들인 것을 확인 할 수 있다.
$id=gogo_test
 $filenum=1
 $no=19

즉, 이 값들을 토대로 해서
어떤 쿼리가 전송되는지 조합해보면
update zetyx_board_gogo_test set download1=download1+1 where no='19';
이러한 쿼리가 만들어져서 보내지는 것을 확인 할 수 있다.

실제로 이런 쿼리가 만들어지는지 확인해보자

먼저
SQL 로그를 기록되게 하기위해서 실행중인 SQL을 끄고 다시 실행하도록 하자.



(sql 종료)



종료 후 옵션을 걸어서 실행해 줄 것이다.

#> mysqld_safe --log=query.log &
로 실행하게 되면
로그가 기록되게 된다.



(로그 기록 실행)



기록되는 로그의 위치는
/var/lib/mysql 에 있는
query.log 파일이다.



(로그 파일 확인)



로그 파일을 열어둔채 다시 다운로드를 클릭하면
아래와 같이 로그가 기록되는 것을 확인 할 수 있다.



(로그 확인)



로그를 보면 우리가 예상했던 update 구문이 정확히 일치한다는 것을 볼 수 있다.
우리의 생각이 맞았던 것이다.

자 그렇게되면
우리가 조작할 수 있는 값은 3개
이 3개로 SQL구문을 Injection 할 수 있다는 것이다.

예를 들어 id변수를 hello로 바꿀 수 있다.



(SQL Injection)




(결과 화면)




그 결과 hello라는 게시판이 없어서 이런 페이지가 나왔고
아무 일이 이러나지 않았다.

두번 째로 no변수를 1로 바꾸어 보았다.




(no 변조)




no 1의 게시 다운로드1 의 횟수가 1 추가되었는지 확인해보자

mysql에 들어가서 아래와 같이 확인해본다.



(확인)



(1 추가)



확인해본 결과 1이 증가했다는 것을 볼 수 있다.

그렇다면 마지막으로 filenum?
$filenum=1=100
으로 하면 어떻게 될까?
그렇게 되면
update zetyx_board_gogo_test set download1=100=download1=100+1 where no='19';
이 되게 되고 문법에 맞지 않아 실행이 되지 않는다.
하지만 이러한 변조는 아마 눈치 챘을 거라 생각한다.
바로 다운로드 횟수 100으로 바꿀 수 있다는 가능성이 있다는 것이다.



(다운로드 회수 100 조작)



하지만 뒤에 문법이 맞지 않는다는 것이 문제다.
그렇다면 뒷 부분을 주석처리할 수 있다면?
주석 : --, /* */, #
예를 들어
update zetyx_board_gogo_test set download1=100#=download1=100#+1 where no='19';
이렇게 되게 하기 위하여
filenum에다가 1=100# 을 넣는다면
뒷부분은 주석처리 되고 다운로드1의 횟수가 100이 되는 SQL 명령이 된다.



(SQL Injection)




(실행 결과)



실행 결과 로그를 확인해보니 #이 빠져있다..!
바로 URL에서 입력할 때 특수 문자는 URL Encoding이 되기 때문이다.

* URL encoding
 - URL에서 사용하는 특수문자
 - 공백: +, %20 (아스키 코드)
 - 문자에 해당하는 아스키코드 앞에 %를 붙여주면된다.
 # -> %23

그렇기 때문에 주석 처리를 위한 # 대신에 %23을 사용한다면?



(%23 사용)



결과 다운로드 수가 100으로 적용된 것을 볼 수 있다.



(100 적용)



그리고 뒤에 where 조건이 없었으므로 아마 모든 글의
다운로드1 횟수가 100으로 됬을 것이다.



(100으로 적용됨)



이 때 사용된 쿼리문을 로그에서 확인해보면



(쿼리 확인)



#이 아까처럼 사라지지 않고
남아서 주석처리 역할을 한것을 볼 수 있다.


그렇다면~?
이러한 SQL Injection을 이용해 XSS공격을 할 수 있을까?
예를들어 update구문을 이용해 게시글에 스크립트를 추가하도록 하는 것이다.
그렇게 되면 스크립트 차단을 해놨어도 우회할 수 있는 길이 생기게 된다.

먼저 게시글이 DB의 어떤 컬럼에 저장되는지 확인한다.



(게시글 내용)



확인해본 결과 게시글은 memo라는 컬럼에 데이터가 저장되는 것을 확인 할 수 있다.

우리는 그냥 update를 사용하면 원래 글 내용이 사라지므로
원래 글 뒷부분에 스크립트를 추가해보겠다.

이를 위해 concat이라는 함수가 있다.
바로 문자열을 연결해주는 함수이다.



(concat)



위 처럼 문자열을 연결해주는 역할을 한다.

우리는 이것을 이용하여
concat(memo, '스크립트') 를 사용할 것이다.

그렇게 되면 아래와 같은 url이 나온다.



(공격에 사용된 url)



실제 적용해보면
예상되는 쿼리는
update zetyx_board_gogo_test set download1=100, memo=concat(memo, '<script>alert(\'attack!!\') </script>')  #=download1=100#+1 where no='19';
이렇다.
게시글 뒷부분에 스크립트 코드가 추가되는 것이다.



(SQL Injection 실행)



실행 후 로그를 확인해보면



(로그 확인)



우리가 예상한 대로 SQL 명령이 전달 된 것을 확인 할 수 있다.

우리가 where 조건을 주지 않았기 때문에
모든 글에 스크립트가 들어가서


(XSS 공격)



XSS 공격 팝업 창이 뜰 것이다.

게시글의 코드를 확인해보면



(스크립트 코드 추가)



스크립트 코드가 추가된 것을 확인 할 수 있다.


간단하게 이러한 Injection을 막기 위해
magic_quotes_gpc 라는 설정을 켜둔다. GPC(Get, Post, Cookie)
하지만 5.4버전부터 이 설정이 사라져있다. (성능 때문에)

설정의 vi /etc/php.ini
파일에서 483번 라인을 On 해주면 된다.



(설정)



이렇게 되면 문자열 입력이 안된다.
한번 해보겠다.
단순히
download.php?id=gogo_test&page=1&page_num=20&category=&sn=off&ss=off&sc=off&keyword

=&prev_no=&select_arrange=headnum&desc=asc&no=19&filenum=1=100,%20me

mo=concat(memo,'injection test !')%20%23
이렇게 게시글에 injection test! 문자열을 추가해볼것이다.



(SQL Injection)



실행 후 SQL 로그를 확인해 보면




(로그 확인)




' 쿼터 앞에 \ (역슬래쉬)가 붙은 것을 볼 수 있다.
그래서 '가 문자열의 역할을 못하게 되어 SQL Injection이 되지 않았다.

=> 문자열 인젝션은 따옴표를 적어줘야하는데
여기서 magic_quotes_gpc 설정을 해주면 문자열 따옴표가 escape되서
문자열을 나타내지 못하게 된다.
그러므로 문자열 인젝션이 불가능하게 된다.
하지만 이걸 켜주게 되면
모든 입력에 대해 전부 체크하게 된다.
-> 쿼리문이 없더라도 입력값을 전부 체크하기 때문에 성능이 떨어지게 된다.
그렇기 때문에 사라진 것이다. 만들때 함수를 이용하여 체크할 수 있도록 해둔 것이다.
=> 설정에서 빠진 이유이다.



오늘은 SQL Injection을 직접 실험해 볼 것이다.

sql injection이란
입력 값에서 비정상적인 문구를 입력하여 비정상적인 동작을 유발시키는 것이다.

오늘 내가 할 것은
로그인 상태에서 비밀번호로 비정상적인 문구를 입력하여
로그인에 성공해 보겠다.

사용할 sql injection의 원리는
입력한 데이터가 서버로 날라가고 코드에 입력되는데
1' or '1 과 같은 값을 입력하게 되면
코드 상에서 항상 참의 값을 유발하여 로그인에 성공하는 것이다.

다른 예로는 주석처리를 이용하여
sql injection을 시행하는 방법이다.
SQL Injection을 통해 받는 응답을 통해서 페이지 코드를 유추하여
정확하고 원하는 동작을 유발하도록 SQL을 Injection 하는 것이다.

SQL Injectino은 실제 서버에서 사용하면 안된다.
(불법!)

이 공격을 수행해보기 위해
서버를 만들어야한다.

간단하게 php 파일을 생성한다.



(php 파일 생성)




(php 파일 작성)



php 파일을 작성한다.

간단히 로그인창을 만들 것이다.
아이디와 비밀번호를 입력받고
인증하는 페이지다.

로그인 페이지 만드는 과정은 (JSP 혹은 Web 프로그래밍 게시판에 정리하겠다.)




(만들어진 페이지 모습)



이제 이 서버에 공격을 해보겠다.

공격에 앞서 방화벽 부터 체크해야한다.

wafw00f는 웹 방화벽 탐지 프로그램이다.



(웹 서버 IP 주소 입력)





(실행 화면)



실행 화면을 보면
웹 방화벽이 탐지 되지 않았다.

즉 공격이 가능하다는 뜻이다.
이제 sql 구문을 입력해 보겠다.




(sql 구문 입력)



sql 구문을 입력한다.



(로그인 성공)



입력한 sql 문구는
1' or '1
이다.

비밀번호가 아님에도 불구하고 로그인에 성공하였다.

공격 성공!
이 공격을 탐지 할 수 있을까?

서버에서 wireshark를 동작시키고있었다.



(wireshark 동작)



패킷 캡쳐를 중단하고
conversations 로 들어가 패킷을 확인해 볼것이다.
많은 양의 패킷의 경우 conversation 항목으로 쉽게 패킷 내용을 볼 수 있다.
(일일이 패킷내용을 보기 힘드므로.)




(conversation 클릭)




(패킷 Follow Stream)



공격자 (120번) 이 접속한 내용을 확인해보겠다.

패킷을 클릭해서 Follow Stream 을 클릭한다.



(패킷 내용)



패킷 내용을 확인해보니
패스워드에 비정상적인 값이 입력된 것을 확인 할 수 있었다.








+ Recent posts