본문 바로가기

웹/CTF study & write up

ctf sutdy #1

ctf 웹 문제를 분석하거나 풀어보면서 공부하는 스터디 글입니다.

 

1. ctf 문제

2020년 X-MAS CTF의 World's most complex SQL Query를 선택해서 라업을 분석했습니다.

라업은 아래의 라업을 보면서 분석했습니다.

eine.tistory.com/entry/Xmas-CTF-2020-write-ups-focus-on-web-challs

 

[Xmas CTF 2020] write ups (focus on web challs)

I participated in X-mas CTF 2020(xmas.htsp.ro/) held from 11st december 2020, to 18th december 2020. X-mas CTF is one of my favorite competition because, it held with long period so that I don't hav..

eine.tistory.com

 

2. 문제에 사용된 취약점 목록

sql injection 문제입니다. 하지만 이 문제는 주어진 sql 쿼리문을 분석해서 푸는 문제인데, 분석해야하는 쿼리문이 문제 이름처럼 굉장히 복잡하게 적혀있습니다.

 

3. 분석

먼저 문제 소스 코드입니다. 

<?php 
if (isset($_GET['source'])) 
{
	show_source("index.php"); die (); 
} 
if (isset($_POST['flag'])) 
{ 
	include ("config.php"); 
    $conn = new mysqli($servername, $username, $password, $dbname);
    $query = "INSERT INTO FLAG (`id`, `n`) VALUES "; 
    $alf = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?!-_{}()";
    header('Ever wondered that Soy could be pronounced: like Soiii? Me neither.'); 
    for ($i=0; $i<strlen($_POST['flag']) && $i < 50; $i++) { \
    	$c = $_POST['flag'][$i]; 
        if (strlen($c) === 1) { 
        	$indx = strpos($alf, $c);
            if ($indx !== FALSE) { 
            	$query .= '(' . $i . ',"' . $alf[$indx] . '"),'; 
            } else { 
            die("Invalid char."); 
            } 
     	} else { 
        die("Invalid input?"); 
        } 
	} 
	$query = substr($query, 0, strlen($query) - 1); 
	mysqli_query($conn, 'DROP TABLE IF EXISTS FLAG'); 
	mysqli_query($conn, 'CREATE TABLE FLAG (`id` int not null primary key, `n` char)');
	mysqli_query($conn, $query); 
	$query = "SELECT o FROM ( SELECT 0 v, '' o, 0 pc FROM (SELECT @pc:=0, @mem:='', @out:='') i UNION ALL SELECT v, CASE @pc WHEN 121 THEN 0 WHEN 70 THEN @pc:=73 WHEN 87 THEN IF(@x3 = 'a', 0, @pc:=89) WHEN 32 THEN @sp := @sp + 1 WHEN 25 THEN @sp := @sp - 1 WHEN 28 THEN @sp := @sp + 1 WHEN 56 THEN @sp := @sp + 1 WHEN 18 THEN IF(BIN(ASCII(@prt)) NOT LIKE '1111011', @pc:=89, 0) WHEN 126 THEN 0 WHEN 17 THEN @prt := (SELECT n FROM FLAG WHERE id = 5) WHEN 12 THEN IF((SELECT n FROM FLAG WHERE id = 2) = 'M', 0, @pc:=80) WHEN 11 THEN IF(@count = @targetsz, 0, @pc:=89) WHEN 103 THEN @sp := @sp + 1 WHEN 41 THEN IF(INSTR(@e, '?') > 0, 0, @pc:=43) WHEN 81 THEN (SELECT @x1 := n FROM FLAG WHERE id = 4) WHEN 49 THEN IF(SUBSTR(@dat, @i - 1, 3) NOT LIKE REVERSE('%tao%'), @pc:=124, 0) WHEN 73 THEN 0 WHEN 82 THEN (SELECT @x2 := n FROM FLAG WHERE id = 5) WHEN 58 THEN @sp := @sp + 1 WHEN 92 THEN 0 WHEN 85 THEN (SELECT @x3 := n FROM FLAG WHERE id = 6) WHEN 64 THEN IF((SELECT FIELD((COALESCE((SELECT GROUP_CONCAT(n SEPARATOR '') FROM FLAG where id in (17, ASCII(@e)/3-3, (SELECT @xx := CEILING(ASCII(@f)/3)+1))), '78')), 'ATT', 'BXX', 'ENN', 'FPP', 'VMM', 'PSS', 'ZEE', 'YDD', 'PPP')) = FLOOR(@xx / 4), 0, @pc:=89) WHEN 95 THEN IF(@n = 0, 0, @pc:=99) WHEN 74 THEN @i := @i + 1 WHEN 68 THEN (SELECT @e := CONCAT_WS('AVION', (SELECT n FROM FLAG WHERE id = @i))) WHEN 78 THEN @out := @ok WHEN 107 THEN @sp := @sp - 1 WHEN 21 THEN @sp := @sp + 1 WHEN 83 THEN IF(@x1 = 'd', 0, @pc:=89) WHEN 104 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',@pc+2,'</m>')) WHEN 31 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',@pc+2,'</m>')) WHEN 122 THEN @sp := @sp - 1 WHEN 102 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',@n - 1,'</m>')) WHEN 45 THEN 0 WHEN 93 THEN @get_arg_tmp := @sp-2 WHEN 26 THEN @prt := (SELECT n FROM FLAG where id = 6) WHEN 86 THEN (SELECT @x4 := n FROM FLAG WHERE id = 7) WHEN 69 THEN IF(INSTR((SELECT IF(ORD(@e) = @i ^ 0x4c, @f, CHAR(@xx*2.75))), '?') = '0', 0, @pc:=71) WHEN 97 THEN @sp := @sp - 1 WHEN 59 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',@pc+2,'</m>')) WHEN 108 THEN @sp := @sp - 1 WHEN 46 THEN @i := @i - 1 WHEN 115 THEN @n:=ExtractValue(@mem,'/m[$@get_arg_tmp]') WHEN 100 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',@n,'</m>')) WHEN 55 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',@prt,'</m>')) WHEN 19 THEN @sp := 1 WHEN 24 THEN @pc:=92 WHEN 33 THEN @pc:=113 WHEN 29 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',87,'</m>')) WHEN 16 THEN IF((@prt SOUNDS LIKE 'Soiii!'), 0, @pc:=80) WHEN 119 THEN IF(ASCII(@n) = @compareto, @pc:=121, 0) WHEN 3 THEN @notok := 'Wrong.' WHEN 42 THEN @pc:=45 WHEN 8 THEN IF(ASCII(@e) ^ 32 = 120, 0, @pc:=89) WHEN 98 THEN @pc:=ExtractValue(@mem,'/m[$@sp]') WHEN 50 THEN (SELECT @i := GROUP_CONCAT(n SEPARATOR '') FROM FLAG Where id in (14, 16, 19, 22, 25, 32)) WHEN 91 THEN @pc:=126 WHEN 117 THEN @compareto:=ExtractValue(@mem,'/m[$@get_arg_tmp]') WHEN 34 THEN @sp := @sp - 2 WHEN 84 THEN IF(@x2 = 'e', 0, @pc:=89) WHEN 37 THEN @i := 13 WHEN 20 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',7,'</m>')) WHEN 63 THEN IF(@rv = INSTR('t35t', 'm4ch1n3'), @pc:=80, 0) WHEN 53 THEN IF(STRCMP((SELECT left(REPLACE(UNHEX(REPLACE(hex(RIGHT(QUOTE(MID(MAKE_SET(40 | 2,'Ook.','Ook?','Ook!','Ook?', 'Ook!','Ook?','Ook.'), 4)), 12)), '4F6F6B', '2B')), ',+', ''), 3)), (SELECT GROUP_CONCAT(n SEPARATOR '') FROM FLAG WHERE id > 28 and id < 32)) NOT LIKE '0', @pc:=89, 0) WHEN 111 THEN @sp := @sp - 1 WHEN 6 THEN IF(@dat = 'X-MAS', @pc:=80, 0) WHEN 80 THEN 0 WHEN 112 THEN @pc:=ExtractValue(@mem,'/m[$@sp]') WHEN 120 THEN @rv := 0 WHEN 90 THEN @out := @notok WHEN 61 THEN @pc:=113 WHEN 43 THEN 0 WHEN 30 THEN @sp := @sp + 1 WHEN 101 THEN @sp := @sp + 1 WHEN 52 THEN IF((SELECT IF(SUBSTR(@dat, (SELECT CEILING(ASCII(ASCII(@F))/2)), 3) = (SELECT NAME_CONST('TAO', 'SQL')), 1, 0)) = FIND_IN_SET(0,'f,e,e,d'), @pc:=124, 0) WHEN 71 THEN 0 WHEN 9 THEN IF((SELECT n FROM FLAG WHERE id = 1) = '-', 0, @pc:=89) WHEN 35 THEN IF(@rv = INSTR('xbar', 'foobar'), @pc:=80, 0) WHEN 62 THEN @sp := @sp - 2 WHEN 2 THEN @ok := 'OK.' WHEN 51 THEN IF(HEX(@i) = REPEAT('5F', 6), 0, @pc:=89) WHEN 88 THEN IF(@x4 = 'd', 0, @pc:=89) WHEN 109 THEN @n:=ExtractValue(@mem,'/m[$@sp]') WHEN 10 THEN (SELECT @count := COUNT(*) FROM FLAG) WHEN 1 THEN @strn := 'MySQL' WHEN 39 THEN 0 WHEN 96 THEN @rv := 1 WHEN 106 THEN @pc:=92 WHEN 114 THEN @get_arg_tmp := @sp-3 WHEN 47 THEN IF(@i > 10, @pc:=39, 0) WHEN 0 THEN @mem:=CONCAT(@mem,REPEAT('<m></m>',50)) WHEN 94 THEN @n:=ExtractValue(@mem,'/m[$@get_arg_tmp]') WHEN 60 THEN @sp := @sp + 1 WHEN 99 THEN 0 WHEN 123 THEN @pc:=ExtractValue(@mem,'/m[$@sp]') WHEN 89 THEN 0 WHEN 38 THEN @l := 0 WHEN 113 THEN 0 WHEN 36 THEN IF((SELECT ELT(BIT_LENGTH(BIN(12))/32, BINARY(RTRIM(CONCAT(REVERSE(repeat(SUBSTR(REGEXP_REPLACE(HEX(weight_string(TRIM(UCASE(TO_BASE64((SELECT CONCAT((SELECT n FROM FLAG WHERE id LIKE '20'), (SELECT n FROM FLAG where id IN ('50', '51', SUBSTR('121', 2, 2)))))))))), 'D', 'A'), -16, 16), 1)), (SELECT SPACE(6))))))) = CONCAT_WS('00','A3','43','75','A4',''), 0, @pc:=89) WHEN 13 THEN (SELECT @f := n FROM FLAG WHERE id = 3) WHEN 44 THEN @l := 1 WHEN 65 THEN @i := 33 WHEN 48 THEN IF(@l > FIND_IN_SET('x','a,b,c,d'), @pc:=89, 0) WHEN 110 THEN @rv := @rv * @n WHEN 125 THEN @out := @notok WHEN 127 THEN 0 WHEN 4 THEN @targetsz := 42 WHEN 5 THEN (SELECT @dat := COALESCE(NULL, NULL, GROUP_CONCAT(n SEPARATOR ''), 'X-MAS') FROM FLAG) WHEN 116 THEN @get_arg_tmp := @sp-2 WHEN 23 THEN @sp := @sp + 1 WHEN 105 THEN @sp := @sp + 1 WHEN 22 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',@pc+2,'</m>')) WHEN 15 THEN @prt := CONCAT((SELECT n FROM FLAG WHERE id = 4), (SELECT n FROM FLAG WHERE id = 7), (SELECT n FROM FLAG WHERE id = 24)) WHEN 14 THEN IF(ASCII(@e) + ASCII(@f) = 153, 0, @pc:=89) WHEN 54 THEN @prt := (SELECT n FROM FLAG whEre id in (CAST((SUBSTR(REPEAT(RPAD(SOUNDEX('doggo'), 2, '?'), 2), 4, 1)) AS INTEGER) * 7 + 1)) WHEN 72 THEN @l := @l + 1 WHEN 77 THEN 0 WHEN 118 THEN @rv := 1 WHEN 27 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',@prt,'</m>')) WHEN 76 THEN IF(@l > LOCATE(FIND_IN_SET('p','abcdefghijklmnoqrstuvwxyz'), '1'), @pc:=124, 0) WHEN 7 THEN (SELECT @e := n FROM FLAG WHERE id = 0) WHEN 40 THEN (SELECT @e := CONCAT((SELECT n FROM FLAG WHERE id = @i))) WHEN 79 THEN @pc:=126 WHEN 124 THEN 0 WHEN 66 THEN @l := 0 WHEN 57 THEN @mem:=UpdateXML(@mem,'/m[$@sp]',CONCAT('<m>',52,'</m>')) WHEN 67 THEN 0 WHEN 75 THEN IF(@i < 41, @pc:=67, 0) ELSE @out END, @pc:=@pc+1 FROM (SELECT (E0.v+E1.v+E2.v+E3.v+E4.v+E5.v+E6.v+E7.v+E8.v+E9.v+E10.v) v FROM(SELECT 0 v UNION ALL SELECT 1 v) E0 CROSS JOIN (SELECT 0 v UNION ALL SELECT 2 v) E1 CROSS JOIN (SELECT 0 v UNION ALL SELECT 4 v) E2 CROSS JOIN (SELECT 0 v UNION ALL SELECT 8 v) E3 CROSS JOIN (SELECT 0 v UNION ALL SELECT 16 v) E4 CROSS JOIN (SELECT 0 v UNION ALL SELECT 32 v) E5 CROSS JOIN (SELECT 0 v UNION ALL SELECT 64 v) E6 CROSS JOIN (SELECT 0 v UNION ALL SELECT 128 v) E7 CROSS JOIN (SELECT 0 v UNION ALL SELECT 256 v) E8 CROSS JOIN (SELECT 0 v UNION ALL SELECT 512 v) E9 CROSS JOIN (SELECT 0 v UNION ALL SELECT 1024 v) E10 ORDER BY v) s) q ORDER BY v DESC LIMIT 1"; mysqli_query($conn, $query, MYSQLI_USE_RESULT); $result = mysqli_query($conn, $query, MYSQLI_USE_RESULT); echo mysqli_fetch_array ($result, MYSQLI_ASSOC)['o']; die(); } ?>
	
	mysqli_query($conn, $query, MYSQLI_USE_RESULT); 
	$result = mysqli_query($conn, $query, MYSQLI_USE_RESULT);
	echo mysqli_fetch_array ($result, MYSQLI_ASSOC)['o']; die(); 
}
?>

소스 코드를 정리하면  위와 같습니다. 여기서 핵심은 엄청나게 킨 쿼리문을 해석해야 한다는 것입니다. 쿼리문을 한 줄 씩 분리하면 아래처럼 정리됩니다.

 

 

여기서 핵심은 이 쿼리문들을 컴퓨터의 program counter(pc)처럼 확인하여 분석을 해야 하는 것이었습니다. when 뒤의 숫자는 컴퓨터 cpu 내부의 pc의 역할을 pc 변수가 수행하고 각 쿼리문은 cpu의 동작처럼 수행하는 구조인 것을 파악해야 합니다. 문제의 쿼리문은 개행이되어 있지도 않고, 처음 봤을 때 이 문제를 cpu 내부의 프로그램 동작과정을 볼 수 있어야 한다는 것이 굉장히 독특했습니다. 

 

여기서 pc란 cpu 내부의 레지스터를 말하는데, 다음 실행될 명령어의 주소를 가지고 있는 포인터라고 생각하면 편합니다. 즉 cpu가 다음 동작할 정보를 가지고 있습니다. 그리고 pc에 저장된 명령어가 실행된 이후에는 다음 실행할 명령어의 주소를 담고 있게 됩니다. 자세한 내용은 아래의 링크를 참조하면 좋을 것 같습니다. (보통 대학교 과정에서는 컴퓨터구조론 등 컴퓨터 내부 레지스터나 부품 들의 동작 과정을 다루는 수업에서 배우게 됩니다.)

egloos.zum.com/rousalome/v/10018576

 

[ARM] 전원을 키면 처음에 어디서 무엇을 실행할까[1]: PC 레지스터

분야를 막론하고 C 언어나 파이썬으로 컴퓨터 프로그래밍을 하다보면 다음과 같은 의문점이 생깁니다. * 바로 컴퓨터의 전원 버튼을 누르면 가장 먼저 무엇을 실행할까? * 이 때 실행되는 어셈블

egloos.zum.com

 

쿼리문을 번호에 맞게(프로그램 순서에 맞게 재배열한 코드가 아래 코드입니다.

이제 pc처럼 한줄 한줄 동작을 따라가면서 분석해보아야합니다. 첫 줄부터 보면 변수의 값 설정부터 다양한 쿼리문들이 존재합니다. 여기서 주목해야할 두 가지 부분이 있습니다.

 

이 부분에서 알아야할 점은 id값에 따라 다른 값을 가져와서 여러가지 플래그 형식이 발생한다는 점입니다. 그리고 이 값에 따라 prt 값이 달라집니다. 하지만 16번(임의로 번호로 이 글에서 부르겠습니다.)에서 Soiii!라는 값이라는 힌트를 주는데 아래의 소스코드와 연결해서 지어보면 플래그 값을 특정할 수 있습니다. 그리고 이를 통해 필요한 id값을 알 수 있습니다.

그리고 두 번째 부분입니다. 

select IF((SELECT ELT(BIT_LENGTH(BIN(12))/32, BINARY(RTRIM(CONCAT(REVERSE(repeat(SUBSTR(REGEXP_REPLACE(HEX(weight_string(TRIM(UCASE(TO_BASE64(( SELECT CONCAT("XY" ) )))))), 'D', 'A'), -16, 16), 1)), (SELECT SPACE(6))))))) = CONCAT_WS('00','A3','43','75','A4',''), "OK", "FAIL")

이 부분은 소스코드에서 제가 못 찾긴했는데 저도 문제 자체를 보지 못하고 라업을 보면서 분석하는 것이기 때문에 이 소스코드 부분이 있는 것 같습니다. 아마도 36번줄의 코드를 조금 수정한 부분으로 추측했습니다. 수정한 쿼리문을 통해 flag값이 저장되어 있는 id값을 알아낸 것 같습니다. 이 부분은 제가 직접 해볼 수도 없고, 라업에도 상세히 적혀 있지 않아서 흐름 상 제 생각을 추측한 것입니다. 그렇게 해서 나온 결과값이 x=flag[20], y=flag[21]이라고 합니다.

 

쿼리문에서 사용한 함수 들을 먼저 설명하면 RTRIM은 문자열 오른쪽의 공백을 제거하고 옵션을 인자로 받을 경우에는 문자열의 오른쪽에 해당 문자가 있을 경우 없애주는 함수입니다. 그리고 TRIM 함수는 RTRIM함수와 같은 기능을 하지만 양쪽의 공백들을 지워주는 함수입니다. REGEXP_REPLACE 함수는 문자열에서 특정 함수를 찾아서 치환해주는 함수의 기능을 합니다. 아래는 참고 블로그 글들입니다.

lee-mandu.tistory.com/40

 

[oracle] 정규식 REGEXP_REPLACE 함수

이 함수는 replace 함수를 확장한 개념으로 주어진 문자역에서 특정 패턴을 찾아서 주어진 다른 모양으로 치환하는 함수이다. 문법 REGEXP_REPLACE (source_char, pattern  [, replace_string  [, position  [..

lee-mandu.tistory.com

gent.tistory.com/194

 

[오라클] TRIM, LTRIM, RTRIM 함수 사용방법 (문자, 공백, 0, 제거)

오라클(Oracle) TRIM, LTRIM, RTRIM 함수 사용방법 TRIM 함수는 문자열의 양쪽 공백을 제거하는 기본적인 함수이다. LTRIM 함수, RTRIM 함수는 왼쪽과 오른쪽의 공백을 제거할 때 사용가능 하지만, 반복적인

gent.tistory.com

.

 

4. 전체 익스코드 분석

짤린 쿼리문은 문제에서 주어진 쿼리문은 36번째 쿼리문입니다.

 

3번에서 사용한 쿼리문을 이용해 얻은 값인 x의 id=20, y의 id=21을 이용하여 계속 플래그 값을 업데이트해줍니다.

 

익스코드를 살펴보면 먼저 brute force에 사용할 문자열을 하나 만들어 두었습니다. 그 후 반복문을 통해 brute forcing 공격을 수행합니다. 이때 flag값을 업데이트를 계속해줍니다. 서버에 flag값이 이미 설정되어 있고 n값을 업데이트 해준 뒤 chk함수의 쿼리문을 실행합니다. chk 쿼리문에서 업데이트된 flag값을 select해온뒤 if문을 통해 판별하여 맞을 경우 해당 값을  출력해주는 익스코드입니다.

 

5. 리뷰

일단 제가 생각했을 때 이 문제의 점수는 7.5점 정도는 되는 것 같습니다. 일단 이 문제의 쿼리문들이 굉장히 복잡하기도 하지만 이 문제를 프로그램 카운터와 연관지어 생각하기가 굉장히 어려울 것이라고 생각합니다. 그리고 쿼리문을 분석하는 과정에 있어서 너무 많은 함수들이 사용되었고, 이 함수들을 하나하나 분석해서 결과값을 예측하는 것이 어렵다고 생각했습니다. 물론 저는 제대로 된 문제를 확인할 수 없는 상황이다 보니 쿼리문들을 직접 넣어보지 못하고 어떤 결과값을 가져오는지 정확하게 알 수 는 없었습니다. 하지만 이 문제 풀이의 전체 흐름을 보았을 때 처음부터 프로그램 카운터의 개념이 모르면 저렇게 정리하기도 쉽지 않을 것이고, 쿼리문 분석에 있어서도 굉장히 어려울 것이라 생각합니다. 그리고 sql 문제라는 것이 함정이 될 수도 생각하는 것이 아직 엄청나게 어려운 문제들을 풀어보지 못한 저로서는 단순히 풀이를 분석하는 것도 굉장히 어려움을 격었습니다. 하지만 조금씩 분석해 나가면서 이해하다보니 절대 못 풀 문제다라는 생각은 들지 않아서 7.5점 정도를 주었습니다.

 

*제가 조금 분석에 부족한 부분이 있을 수 있습니다. 그리고 쿼리문을 완벽하게 분석하지는 못 했습니다.(전체적인 흐름과 문제를 푼 풀이자의 의도, 방향등은 분석했으며 어떤 방법으로 풀이를 했는지는 이해했습니다.) 결정적인 쿼리문 부분에 대한 분석은 제가 이번주 안으로 조금 더 내용을 추가할 수 있도록 하겠습니다.

' > CTF study & write up' 카테고리의 다른 글

ctf study #6  (0) 2021.02.24
ctf study #5  (0) 2021.02.17
ctf study #4  (0) 2021.02.15
ctf study #3  (0) 2021.02.11
ctf sutdy #2  (0) 2021.02.09