2018 코드게이트 대학부 예선전을 치뤘다.

웹쪽을 공부해 왔기때문에 web 파트는 내가 풀었지만

simple_cms 한문제를 16시간 보고도

풀지 못했다.....

하지만 이 simple_cms문제는 총 6팀만 푼 문제로 

상당히 난이도가 있었기 때문에 review를 해본다.

우선 첫 화면은 로그인 페이지 이다.

그리고 

click me if you don't have a count를 누르면


회원 가입 화면이 나온다.

여기서 가입을 하고 난 후에

board로 들어가 보면

이렇게 write를 눌러서 사람들이 쓰는 글들이 

계속해서 업데이트 된다.

실시간으로


코드게이트는 소스를 주기 떄문에 조금 더 나을 수 있지만

이번 Simple_CMS는 굉장히 많은 소스코드를 가지고 

문제를 풀어나가야 하기때문에

취약점을 찾는 것 부터가 매우 어려웠다.

아마 취약점을 잡는 것이 이 문제의 핵심이라고

말할수 있을 것 같다.



우선 simple_cms.sql 파일에서

우리는 무엇을 찾아야 하는지를 알 수 있다.

db가 만들어 질 때에

board 와 flag 테이블이 만들어 지는데

이때 board와 flag테이블은 앞에 {table_prefix}라는 문장을 앞에 붙이게 된다.

그리고 flag의 values에

{blind_column}이라는 알 수 없는 문자열이 앞에 붙는다.

아마 우리는 {table_prefix}를 찾아야 할 것 같다.



우선 입력창에는 항상

filter_str의 필터링이 있다.

or, and, information, schema, procedure analyse, order, by, group, into 가 필터링이 되어있다.

그러니 거의 injection코드는 다 필터링이 되어있는 셈이다.

이때부터 조금 위축 되었던 것 같다.



나는 소스코드만 한참 뒤지다가

board.class.php의

action_search의 소스코드에서

col에 유난히 많은 필터링이 있는 것을 보고

한번 코드를 파보았다.


우선 

$query = get_search_query($column,$search,$operator)와

$result = DB::fetch_multi_row('board','','','0,10','date desc',$query)

에 대하여 알아보기 위해서


lib.php에서

소스를 보니

$column에는 다중으로 파라미터 삽입이 가능하다.

구분은 | 연산자로 해준다고 되어 있다.

그리고 

LOWER({column[$i]}) like '%{search}%'{operator}"에 변수로 들어가는데

이때 operator는 or 나 and 인데

$type변수에 1이면 or 2이면 and라고 다른 소스에 적혀있다.

그리고 column이 여러개 이면

operator로 연달에 $result문에 붙게 된다.

여기서 나는 여기가 취약점이라는 생각을 확실시 하게 된다.





그리고 DB.class.php에서 

fetch_multi_row함수를 찾을 수 있었다.

fetch_multi_row함수는 get_search_query에서 파라미터 $query를 받고

여기서 우리가 찾고자 하는 난독화된 board명을 만든다.

그리고 우리가 받았던 $query를 foreach문으로

$key값과 $value값으로 만든다.

그리고 {$key}='{$value}'{operator}로 만들어 진다.

그리고 다 만들어 지면

쿼리문을 실행시키는 구조인 것 같다.




우선 search키를 누르면 주소창에 get방식으로 

?act=board&mid=search&col=title&type=1&search=으로 뜬다.

우리는 search와 col에 들어갈 파라미터를 적절히 고쳐야 할 것이다.

여기까지에서 나는 막혔다.

그리고 여러 write up을 보고 공부해보니

우선 title 뒤에 %23(#) 주석을 달아줘야 했다.

물론 이 방법은 시도 해 보았었다.

이렇게 나온다.


그러면 우리는 

?act=board%mid=search&col=title%23&type=1&search=1%0a)%3c0%20union%20select%201,

(select%20table_name%20from%20mysql.innodb_table_Stats%20limit%202,1),3,4,5%23

의 쿼리문을 넣어주면 


이렇게 flag앞에 붙은 table_prefix를 알 수 있다.

우선 나는 table_name을 쓰는 것은 알 수 있었지만

%0a로 우회 가능한 점은 당연히 안될 줄 알았었다.

하지만 %23이 한줄 주석이라는 점을 이용해 %0a로 열을 바꾸어 주어

주석을 피하고 난 후에

union select로 injection을 해주는 방법은 보고나서 아~ 하는 소리를 내기에 충분했다.


그리고 나는 이후에 왜 이렇게 상세하게 테이블의 갯수를 가르쳐 준지 알게되었다.

order by를 막아놓았기 때문에 union select 문을 쓰려면 컬럼의 갯수를 정확히 알아야 한다.

그래서 이렇게 컬럼의 갯수를 준게 아닐까 생각된다.

이후에는 table명을 알았으니

일사 천리로

?act=board&mid=seach&col=title%23&type=1&search=test%0a<0%20union%20(select%201,t.*%20from%20mysql.user%20join%2041786c497656426a6149_flag%20t)%23

문을 넣으면

우리는 flag를 얻을 수 있다.

우선 쿼리문을 분석하자면

flag와 (1,t.*)을 join해서 그 문장을 union select문으로 injection한다.

t.*는 flag문을 t로 지칭해주는 뒷부분으로 flag의 컬럼을 다 사용하는데 이때

컬럼의 갯수가 board의 컬럼갯수보다 하나 부족하기 때문에

앞에 1을 하나 붙여준다.



참조:https://ctftime.org/writeup/8619


'CTF > CTF 문제들' 카테고리의 다른 글

Defcon2019 Speedrun-001 write-up  (0) 2019.05.18
코드게이트2018 후기  (0) 2018.04.09

+ Recent posts