반응형

 


1. 문제 설명


 

 

이번 문제도 SQLi 문제이다.

 

문제를 클릭해보자.

 

문제를 클릭하면 다음과 같은 화면을 볼 수 있었다.

 

 

 

이게 뭐지?

 

done! (0/70)??

 

뭔가 시도 횟수를 의미하는것 같았다.

 

역시나 뭐 별 다른 기능이 없어 view-source를 클릭해서 소스코드를 보았다.

 

역시나 소스코드가 길었다.

 

이럴때는 하나하나 분석을 해야한다.

 

$agent=trim(getenv("HTTP_USER_AGENT"));

$ip=$_SERVER['REMOTE_ADDR'];

if(preg_match("/from/i",$agent))
{
  echo("<br>Access Denied!<br><br>");
  echo(htmlspecialchars($agent));
  exit();
}

 

먼저 getenv 함수를 사용해서 HTTP_USER_AGENT 값을 가져온다.

 

가져온 값에서 trim 함수를 이용해서 나온 값을 agent라는 변수에 담는다.

 

그럼 getenv 함수와 trim 함수를 알아보자.

 

 

getenv 함수는 위와과 같이 정의 되어있다.

 

getenv 함수는 환경 변수의 값을 가져오는 함수이다.

 

그리고 인자 값에 가져오고 싶은 환경 변수 값을 적어주는데

 

문제에서는 "HTTP_USER_AGENT"가 인자로 들어가있다.

 

HTTP_USER_AGENT웹 사이트를 접속한 컴퓨터의 웹 브라우저 정보를 의미한다.

 

다음은 trim 함수이다.

 

 

trim 함수는 whitespace공백strip 하는 함수이다.

 

하지만 문장 가운데 존재하는 공백이 아니라 처음 시작과 끝의 공백을 제거하는 함수이다.

 

예를 들어,  __hello__라는 문장이 있다고 가정하자.

 

 

하지만 trim 함수를 사용하면 처음 시작 2개의 공백과 끝의 2개의 공백을 제거하여

 

다음과 같은 문장이 나온다.

 

 

여기까지 정리하자면 접속한 웹 브라우저의 정보를 읽어와 앞 뒤 공백을 제거한다는 의미이다.

 

다음 코드는 $ip=$_SERVER['REMOTE_ADDR'] 인데

 

REMOTE_ADDR는 웹 사이트에 접속한 컴퓨터의 ip 주소를 가져온다.

 

그 다음 preg_match 함수가 또 보인다. (preg_match -> 클릭)

 

regexp 하는 역할을 하는데 agent라는 변수에 들어간 웹 브라우저 정보에서 from을 필터링한다.

 

다음 코드를 보자.

 

$db = dbconnect();

$count_ck = mysqli_fetch_array(mysqli_query($db,"select count(id) from chall8"));

if($count_ck[0] >= 70)
{ 
  mysqli_query($db,"delete from chall8"); 
}

$result = mysqli_query($db,"select id from chall8 where agent='".addslashes($_SERVER['HTTP_USER_AGENT'])."'");

$ck = mysqli_fetch_array($result);

 

웹 브라우저 정보에 "from"이라는 단어가 없으면 dbconnect 함수를 이용해서 db에 접속한다.

 

그 후 다음과 같은 쿼리로 DB에 쿼리를 날린다.

 

말 그대로 chall8이라는 테이블에 id의 갯수가 몇개있는지 묻는 쿼리이다.

 

그 후 countid의 갯수를 count_ck라는 변수에 넣고

 

count_ck70개 이상이면 chall8 테이블을 삭제한다.

 

만약에 70개 미만이면 코드가 밑으로 내려와 DB에 다음과 같은 쿼리로 묻는다.

 

 

위의 쿼리의 의미를 해석 하기 전에 addslashes 함수에 대해서 알아보자.

 

addslashes 함수에 대한 설명은 여기에 자세히 설명이 되어있다. (클릭)

 

$_SERVER['HTTP_USER_AGENT']유저의 브라우저 접속환경을 의미한다.

 

브라우저 접속환경이 왜필요할까?

 

서버의 입장에서는 브라우저별로 스타일을 다르게 보여지게 하거나 필요한 동작이 다를 수 있다.

 

무튼 다시 쿼리문을 정리하자면 다음과 같이 정리할 수 있다.

 

 

그럼 해석 해보자면 chall8이라는 테이블에서 어떤 사용자의 user_agent 값이 있다면

 

user_agent 값을 가지는 id를 보여달라는것이다.

 

만약 나의 idtest이고 user_agent값이 123123이라면

 

chall8이라는 테이블에서 agent의 값이 123123id는 누구냐라는 의미이다.

 

쿼리상 문제가 없으면 id값(test)이 이제 ck라는 변수에 들어가게된다.

 

다음 코드를 보자.

 

if($ck)
{
  echo "hi <b>".htmlentities($ck[0])."</b><p>";
  
  if($ck[0]=="admin")
  {
    mysqli_query($db,"delete from chall8");
    solve(8);
  }
}

if(!$ck)
{
  $q=mysqli_query($db,"insert into chall8(agent,ip,id) values('{$agent}','{$ip}','guest')") or die("query error");
  echo("<br><br>done!  ({$count_ck[0]}/70)");
}

 

ck 변수에 이제 값(id)이 있다면 누구인지 echo 함수를 이용해서 알려주고

 

만약에 admin이라면 chall8이라는 테이블을 삭제 시킨 후

 

solve(8)이라는 함수를 실행 시키면서 문제를 clear 할 수 있다.

 

하지만 ck라는 변수 즉 user_agent로 조회를 했을때 id가 없다면 insert 함수를 이용해서

 

chall8이라는 테이블에 agent, ip, id 값을 각각 넣어준다.

 

여기까지 정리하면 다음과 같다.

 

해당 문제는 idadmin으로 맞춰줘야지만 solve(8) 함수를 실행시킬 수 있다.

 

그렇기 위해서는 adminuser_agent 값을 알아야한다.

 

그 이유는 id를 뽑기 위해서는 밑의 쿼리를 사용해야하기 때문이다.

 

 

하지만 우리는 adminuser_agent 값을 모른다.

 

아니면 chall8이라는 테이블에 admin이라는 id를 가진 값이 없을 수도 있다.

 

따라서 adminuser_agent 값을 알아내던, chall8이라는 테이블에 idadmin을 생성해줘야할것같다.

 


2. 문제 풀이


 

이번 문제를 풀기 위해선 2개의 가정을 생각해보았다.

  1. chall8이라는 테이블에 id가 admin이 있을 경우 -> admin의 user_agent 값을 알아내야함 or 우회
  2. chall8이라는 테이블에 id가 admin이 없을 경우 -> chall8에 id가 admin을 insert 해야함

위의 처럼 정리 해보았다.

 

확률상 1번일 확률은 적다고 생각했다.

 

왜냐하면 php 코를 봤을 때 adminuser_agent를 알아내기 위한 방법이 현재로써는 보이지 않았다.

 

따라서 2번의 가정으로 문제를 풀어봤다.

 

그 이유는 php 코드를 봤을때 아래의 그림처럼 insert문이 존재했기 때문이다.

 

 

그리고 insert에서 사용자의 입력이나 값이 들어가는곳은 2곳이다.

 

agentip이다.

 

ip는 서버에 접속한 클라이언트의 ip를 $ip=$_SERVER['REMOTE_ADDR']로 설정하기 때문에

 

ipSQLi를 하기는 어렵다고 생각하고 agent 변수에 

 

즉, user-agent 값을 수정해서 insert문을 이용해서 id가 'guest'가 아닌 'admin'으로 수정하려고한다.

 

user-agent 값을 수정하기 위해서는 web-proxy를 사용해야한다.

 

이렇게 웹 프록시로 보면 User-Agent에 접속한 브라우저나 환경 값들이 들어가 있다.

 

여기에 SQLi를 수행할것이다.

 

먼저 SQLi가 잘 먹히는지 확인하기 위해

 

프록시를 사용하여 User-Agentguest01이라고 입력하고 서버로 전송해보자.

 

 

 

chall8 테이블에 guest01이라는 user-agent값이 없으니 아래의 SQLiinsert할것이다.

 

그렇다면 다시 guest01를 그대로 user-agent 값에 넣어서 서버로 전송하면 어떻게 될까?

 

chall8guest01이 라는 user-agentinsert 되었기 때문에 "hi guest"라는 문구를 볼 수가 있다.

 

그렇다면 이제 user-agent를 변경해서 insert가 되는것을 확인했으니

 

chall8에 존재하지않는 user-agent 값과 뒤의 'guest' 부분을 'admin'으로 바꿔주고

 

다시 insert 했을때 사용했던 user-agent 값으로 변경하여 서버로 전송하면

 

"hi guest"가 아닌 admin이 될것이다.

 

페이로드는 다음과 같다.

 

이렇게 SQL문을 만들 수 있다.

 

뒤에 #이라고 넣어준 이유는 뒤에 남아 있는 원래의 SQL문을 주석 처리하기 위함이다.

 

 

이렇게 SQL문을 만들었으면 이제 직접 프록시를 이용하여 작성한 페이로드를 넣어보자.

 

 

guest02라는 새로운 user-agent 값과 아이피 주소, 그리고 guest가 아닌 admin을 넣어준다.

 

 

 

그 후 정상적으로 chall8이라는 테이블에 등록되었다는 done!이라는 문구를 볼 수 있다.

 

다시 guest02로 요청을 한다.

 

 

요청을 하게 되면 서버에서 clear 했다는 문구를 볼 수 있다.

 

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

10. old-10  (0) 2021.11.08
9. old-09  (0) 2021.11.08
7. old-07  (0) 2021.11.04
6. old-06  (0) 2021.11.03
5. old-05  (0) 2021.11.01
반응형

 

이번 문제는 아마 코딩으로 푸는 문제인 것 같다.

 

이제 저 모양이 어떤 문제를 의미하는지 알 것 같다.

 

문제를 클릭해보자.

 

 

클릭하면 view-sourceID: guest, PW: 123qwe가 보인다.

 

입력하는 폼과 URL에 파라미터도 보이지 않기 때문에 view-source를 클릭하여 힌트를 찾아보자.

 

클릭하면 엄청나게 긴 PHP 코드가 나온다.

 

코드는 다음과 같다.

 

<?php
include "../../config.php";
if($_GET['view_source']) view_source();
if(!$_COOKIE['user']){
  $val_id="guest";
  $val_pw="123qwe";
  for($i=0;$i<20;$i++){
    $val_id=base64_encode($val_id);
    $val_pw=base64_encode($val_pw);
  }
  $val_id=str_replace("1","!",$val_id);
  $val_id=str_replace("2","@",$val_id);
  $val_id=str_replace("3","$",$val_id);
  $val_id=str_replace("4","^",$val_id);
  $val_id=str_replace("5","&",$val_id);
  $val_id=str_replace("6","*",$val_id);
  $val_id=str_replace("7","(",$val_id);
  $val_id=str_replace("8",")",$val_id);

  $val_pw=str_replace("1","!",$val_pw);
  $val_pw=str_replace("2","@",$val_pw);
  $val_pw=str_replace("3","$",$val_pw);
  $val_pw=str_replace("4","^",$val_pw);
  $val_pw=str_replace("5","&",$val_pw);
  $val_pw=str_replace("6","*",$val_pw);
  $val_pw=str_replace("7","(",$val_pw);
  $val_pw=str_replace("8",")",$val_pw);

  Setcookie("user",$val_id,time()+86400,"/challenge/web-06/");
  Setcookie("password",$val_pw,time()+86400,"/challenge/web-06/");
  echo("<meta http-equiv=refresh content=0>");
  exit;
}
?>
<html>
<head>
<title>Challenge 6</title>
<style type="text/css">
body { background:black; color:white; font-size:10pt; }
</style>
</head>
<body>
<?php
$decode_id=$_COOKIE['user'];
$decode_pw=$_COOKIE['password'];

$decode_id=str_replace("!","1",$decode_id);
$decode_id=str_replace("@","2",$decode_id);
$decode_id=str_replace("$","3",$decode_id);
$decode_id=str_replace("^","4",$decode_id);
$decode_id=str_replace("&","5",$decode_id);
$decode_id=str_replace("*","6",$decode_id);
$decode_id=str_replace("(","7",$decode_id);
$decode_id=str_replace(")","8",$decode_id);

$decode_pw=str_replace("!","1",$decode_pw);
$decode_pw=str_replace("@","2",$decode_pw);
$decode_pw=str_replace("$","3",$decode_pw);
$decode_pw=str_replace("^","4",$decode_pw);
$decode_pw=str_replace("&","5",$decode_pw);
$decode_pw=str_replace("*","6",$decode_pw);
$decode_pw=str_replace("(","7",$decode_pw);
$decode_pw=str_replace(")","8",$decode_pw);

for($i=0;$i<20;$i++){
  $decode_id=base64_decode($decode_id);
  $decode_pw=base64_decode($decode_pw);
}

echo("<hr><a href=./?view_source=1 style=color:yellow;>view-source</a><br><br>");
echo("ID : $decode_id<br>PW : $decode_pw<hr>");

if($decode_id=="admin" && $decode_pw=="nimda"){
  solve(6);
}
?>
</body>
</html>

 

핵심 코드들을 분석해보자.

 

if(!$_COOKIE['user'])
{
  $val_id="guest";
  $val_pw="123qwe";
  
  for($i=0;$i<20;$i++)
  {
    $val_id=base64_encode($val_id);
    $val_pw=base64_encode($val_pw);
  }
  
  .....
  
}

 

먼저 $val_id 변수에 "guest"$val_pw"123qwe"로 초기화시켜준다.

 

이것은 문제를 클릭하였을 때 볼 수 있었던 것과 동일하다.

 

그 후 for문으로 20번 id와 pw를 즉, "guest"와 "123qwe"를 base64 encode를 수행한다.

 

for문이 끝나면 "guest", "123qwe" 이 두 값을 20번 base64 encode 값이 들어가 있다.

 

공부하는 김에 base64가 먼지 짧게 알아보고 가자.


Base64

 

먼저 인코딩(encoding)이란, 파일에 저장된 정보의 형태나 형식을 데이터 표준화, 보안, 처리 속도 등을 위해 다른 형태로 변환하는 과정을 말한다.

 

특히 이메일, 동영상, 이미지 영역에서 많이 사용되며, 반대로는 디코딩(decoding)이라고 한다.

 

그중 Base64도 인코딩의 한 종류이며 말 그대로 64진법이라는 뜻이다.

 

쉽게 말해 데이터를 64진법으로 표현하는 방식이다.

 

2진법은 0,1로 2가지, 10진법은 0~9까지 10가지로 64진법은 총 64개로 표현이 가능하다.

즉 , 6bit로 문자 한 개를 표현할 수 있다는 의미이다.

 

따라서 8bit의 데이터(바이너리)를 6bit의 크기로 표현한다는 의미이고

 

24bit(3byte)를 단위로 하여 3개의 문자에서 4개의 문자를 얻게 된다.

 

이렇게 되면 3개의 문자에서 4개의 문자로 데이터의 양이 많아진다.

 

그럼 데이터의 양이 증가하는 데 사용하는 이유가 뭘까?

 

바로 통신과정에서 바이너리 데이터의 손실을 막기 위해서 사용한다.

 

간단한 예로 바이너리 파일의 원본 파일이 1,000byte가 있지만 이 데이터를 string 형태로 저장하면

 

1,406byte로 저장된다.

 

반대로 string 형태가 아닌 binary로 저장하면 1,000byte가 그대로이다.

 

이는 char 데이터는 non-signed 1byte : 양의 정수 (0~255 표현)

 

​byte 데이터는 signed 1byte : 정수 (-128~127 표현)

 

즉 표현할 수 있는 데이터의 범위를 넘어서 데이터 손실이 일어나는 것이다.


따라서 이렇게 데이터의 손실을 막고 안전하고 정확하게 데이터를 전송하기 위해 Base64를 많이 사용한다.


다시 본론으로 돌아와 다음 코드를 보자.

 

위에서 20번 base64를 돌린 값을 replace 해주는 방식이다.

 

예를 들어 20번 돌린 base64 값에 1이 있으면 !로, 2가 있으면 @로 replace 해주는 코드이다.

 

val_pw로 마찬가지다.

 

....

  $val_id=str_replace("1","!",$val_id);
  $val_id=str_replace("2","@",$val_id);
  $val_id=str_replace("3","$",$val_id);
  $val_id=str_replace("4","^",$val_id);
  $val_id=str_replace("5","&",$val_id);
  $val_id=str_replace("6","*",$val_id);
  $val_id=str_replace("7","(",$val_id);
  $val_id=str_replace("8",")",$val_id);
  
 ....

 

그다음 아래의 코드를 볼 수 있는데 Setcookie 함수를 이용해서

 

"user"라는 쿠키명에 replace를 거친 값을 넣어주는 것이다.

 

마찬가지로 "password" 쿠키명에 replace를 거친 값을 넣어준다.

 

....

  Setcookie("user",$val_id,time()+86400,"/challenge/web-06/");
  Setcookie("password",$val_pw,time()+86400,"/challenge/web-06/");
  
 ....

 

그럼 그렇게 쿠키값이 있는지 확인해보자.

 

 

위의 쿠키 값을 URL-decoding을 하고 보면 replace가 된 @를 볼 수 있다.

 

 

이처처럼 20번의 base64 econdingreplace가 잘 수행된 것을 확인할 수 있다.

 

다음 코드를 보자.

 

....

$decode_id=$_COOKIE['user'];
$decode_pw=$_COOKIE['password'];

....

 

아까 그림에서 확인한 user라는 쿠키 값과 pwassword라는 쿠키 값을

 

각각 decode_iddecode_pw에 초기화한다.

 

....

$decode_id=str_replace("!","1",$decode_id);
$decode_id=str_replace("@","2",$decode_id);
$decode_id=str_replace("$","3",$decode_id);
$decode_id=str_replace("^","4",$decode_id);
$decode_id=str_replace("&","5",$decode_id);
$decode_id=str_replace("*","6",$decode_id);
$decode_id=str_replace("(","7",$decode_id);
$decode_id=str_replace(")","8",$decode_id);

$decode_pw=str_replace("!","1",$decode_pw);
$decode_pw=str_replace("@","2",$decode_pw);
$decode_pw=str_replace("$","3",$decode_pw);
$decode_pw=str_replace("^","4",$decode_pw);
$decode_pw=str_replace("&","5",$decode_pw);
$decode_pw=str_replace("*","6",$decode_pw);
$decode_pw=str_replace("(","7",$decode_pw);
$decode_pw=str_replace(")","8",$decode_pw);

....

 

처음 replace는 숫자를 문자로 replace 하였지만 지금은 다시 문자를 숫자로 replace 해준다.

 

....

for($i=0;$i<20;$i++)
{
  $decode_id=base64_decode($decode_id);
  $decode_pw=base64_decode($decode_pw);
}

....

 

그 후 for문으로 20번 decode준 값을 $decode_id$decode_pw에 초기화해준다.

 

이러면 원래의 평문이 나올 것이다.

 

....

echo("<hr><a href=./?view_source=1 style=color:yellow;>view-source</a><br><br>");
echo("ID : $decode_id<br>PW : $decode_pw<hr>");

if($decode_id=="admin" && $decode_pw=="nimda")
{
  solve(6);
}

....

 

그 후 echo로 decoding 된 평문의 IDPW를 출력해준다.

 

이때 출력하는 ID와 PW가 "admin", "nimda"이면 solve(6)이 실행되면서 이번 문제를 clear 할 수 있다.

 

여기까지 무리 없이 이해가 되었다면

 

user와 password 값에 admin, nimda를 20번 encoding 한 후

 

replace 한 값을 쿠키 값에 넣어주고 새로고침을 하면 문제를 해결할 수 있을 것 같다.

 

그런데 조금만 생각해보면 replace는 안 해도 된다.

 

어차피 replace로 원래의 "admin", "nimda" base64 값을 구하기 때문에 

 

그냥 "admin", "nimda" 값을 base64 encoding을 20번 값을 쿠키값에 넣으면 된다.

 

바로 코드를 작성해보자.

 

 

코드는 이처럼 짧다.

 

나온 값을 쿠키에 넣고 새로 고침을 하면 

 

 

ID와 PW에 "admin", "nimda"가 출력되면서 6번 문제를 해결할 수 있다.

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

8. old-08  (0) 2021.11.05
7. old-07  (0) 2021.11.04
5. old-05  (0) 2021.11.01
4. old-04  (0) 2021.11.01
3. old-03  (0) 2021.10.19
반응형

3번 문제도 2번 문제와 같이 DB 그림이 있다.

 

따라서 이번 문제도 SQLi를 이용해서 문제를 푸는것 같다.

 

클릭해보자.

클릭해보니 이렇게 이상한 판이 나왔다.

 

처음에는 뭘까?하고 숫자가 입력이 되어있길래 나도 숫자를 입력해야하나? 싶어서

 

빈칸을 클릭해보았다.

 

근데 아래의 5*5만 클릭이 되었다.

 

 

숫자를 입력하는줄알았지만 이렇게 뭔가 규칙을 맞춰야하는것 같다.

 

하지만 아직도 지뢰찾기나 이런 규칙이 있는 퍼즐? 퀴즈? 같은걸 별로 안좋아한다.

 

따라서 위의 그림처럼 그냥 다 클릭하고 solved라는 버튼을 클릭해보았다.

 

solved를 클릭하면 GET 형식으로 다음과 같은 URL로 서버로 요청한다.

 

https://webhacking.kr/challenge/web-03/index.php?_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=1&_8=1&_9=1&_10=1&_11=1&_12=1&_13=1&_14=1&_15=1&_16=1&_17=1&_18=1&_19=1&_20=1&_21=1&_22=1&_23=1&_24=1&_25=1&_answer=1111111111111111111111111

 

그리고 브라우저에서는 다음과 같은 모습을 볼 수 있다.

 

 

여기까지 정리하면

 

총 5*5 빈칸 하나가 의미하는건 URL의 파라미터에 _1, _2 ... 이렇게 표현이 되는것 같다.

 

예를들어 첫번째칸은 파라미터 _1...

 

그리고 클릭을 해서 검은색으로 되면 파라미터 값은 1이 되고 클릭이 안되면 0이 되는것 같다.

 

그리고 5*5이기 때문에 _1,_2...._25를 전부 합친것이

 

answer=1111111111111111111111111로 표현되는것 같다.

 

페이지 소스를 봐도 go()라는 함수가 사용자가 클릭한 칸을 전부 합쳐서

 

문자열로 1111111111111111111111111을 만들어 주는것 같다.

 

그래서 _1과 _2..._25에 SQLi를 수행 보았다.

 

근데 별 다른것이 없었다...

 

마찬가지로 answer에도 SQLi를 수행 보았다.

 

여기서도 별 다른 반응이 없었다.

 

그럼 어떻게 할까?

 

저 퀴즈를 풀어야하나???

 

근데 풀기가 너무 귀찮아서.

 

brute force로 그냥 보내버릴까? 해서 코드를 작성을 해보았다.

 

근데 짜던 도중 생각해보니 표현 할 수 있는 숫자는 0, 1이고 자릿수는 25자리다.

 

그럼 brute force를 수행한다면...  엄청 낭비다..

 

따라서, 퀴즈를 풀어야할것같다...

 

뭐 여러 사이트를 참조하고 문제를 푸는 방법을 익힌 후 문제를 풀고 solved를 눌러보았다.

 

 

이렇게 Clear 했다는 문구와 함께 log를 남기기 위해 이름을 입력하라는 문구가 나오고

 

submit 버튼이 나왔다.

 

따라서 test!!라고 입력을 해보았다.

 

test!!라고 입력해본 결과 입력한 이름 test!!과 아까 퀴즈에서 풀었던 answeriplog처럼 남겨졌다.

 

log을 남기기 위해 submit을 클릭했을때 발생하는 패킷은 다음과 같다.

 

 

POST 형식으로 answer에는 아까 퀴즈에서 푼것이 들어가있으며

 

파라미터 id에는 앞에서 입력한 test!!가 URL 인코딩 되어 있다.

 

여기까지 정리해보자면

 

URL: https://webhacking.kr/challenge/web-03/index.php?_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=1&_8=1&_9=1&_10=1&_11=1&_12=1&_13=1&_14=1&_15=1&_16=1&_17=1&_18=1&_19=1&_20=1&_21=1&_22=1&_23=1&_24=1&_25=1&_answer=1111111111111111111111111

  1. 위의 URL 파라미터에서는 SQLi가 발생하는 곳은 없었다.
  2. 퀴즈를 풀고 나니 log를 남길 수 있는 곳을 찾았다.
  3. log를 남길때 발생하는 패킷에 answer과 id라는 파라미터 2개가 생겼다.
  4. 3번에서 찾은 파라미터 2개에 대해서 SQLi를 수행해보자.

 

이렇게 answer의 마지막에 SQLi를 테스트하기위해 '를 입력하고 서버로 요청해보았다.

 

 

 

이렇게 query error!라는 문구와 함께 오류가 난다.

 

여기다! (Point 1)

 

여기서 SQLi를 수행하면 될것 같다.

 

 

이렇게 answer에 1'+and+1=1#이라고 입력을 해주었다.

 

하지만 이렇게 빈 페이만 나온다.

 

쿼리가 오류이면 분명 query error!라는 문구가 보일 것인데 보이지가 않았다.

 

다음은 answer에 1'+and+1=0#을 입력해보았다.

 

1'+and+1=1#나 1'+and+1=0#이 해당 쿼리를 만들어낸 과정은 다음과 같다.

 

  1. 원래의 answer 끝에 '를 삽입하자 query error!라는 문구가 보임.
  2. 따라서 여기서 SQLi를 수행.
  3. '를 삽입했으니 원래의 정상적인 쿼리 마지막에는 '가 한개가 남음.
  4. 이 마지막 한개의 '를 처리하기 위해 주석인 #을 추가함.

 

결과는 또 빈페이지...

 

뭘까? 하고 생각을 해보았다.

 

log에서는 3개를 볼 수있었다.

 

입력한 이름(id)와 answer 마지막으로 ip 이렇게 3개를 볼 수 있었는데

 

내가 입력한 answer 1'+and+1=1#, 1'+and+1=0#이 잘 먹힌지를 보기위해서는

 

먼저 log에 answer1이라는것을 먼저 등록을 해주어야 참/거짓의 반응을 살펴 볼 수 있을것 같다.

 

이렇게 answer에 1을 넣고 id에는 test를 넣고 먼저 서버로 보내 log를 남겼다.

 

서버에서는 쿼리상에 문제가 없기 때문에 이렇게 log를 남겨준다.

 

이제 다시 answer1'+and+1=1#, 1'+and+1=0#이 잘 먹히는지 살펴보자.

 

 

위의 그림에서 answer 부분이 1' and 1=1# and 기준으로

 

왼쪽 오른쪽이 참이기 때문에 위에서 남긴 로그가 나와야한다.

 

이렇게 잘나왔다.

 

이제 1'+and+1=0#를 입력하고 서버로 요청해보자.

 

여기서 만약에 빈페이지가 나온다면 여기서 SQLi를 수행하는것이 확실해 진다!

 

빈페이지가 나오는것으로 보아 여기서 SQLi를 수행하는것이 확실해 졌다.

 

따라서 앞의 문제처럼 DB명의 길이DB명을 알아보자!!

 

코드를 작성하여 DB의 길이를 알아내보니 10자리였다.

 

이제 DB의 길이를 구하고 이제 DB 명을 알아내야한다.

 

근데 가만히 생각을 해보니 이번 문제가 DB명을 알아내고 table명, column명을 알아낼 필요가 있을까?

 

뭔가 위의 과정을 통해 answer라는곳에 SQLi를 하는건 맞는거같은데..

 

flag값처럼 출제자가 원하는 answer라는 값을 알아내야하는건가?

 

그러면 DB명table명, column명을 알아내서 answer라는 값을 알아내야하는건 맞다.

 

근데 answer라는 값을 항상 참으로 만들면???

 

그래서 answer에 아래와 같이 1'or+'1를 넣어봤다.

 

풀렸다...

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

6. old-06  (0) 2021.11.03
5. old-05  (0) 2021.11.01
4. old-04  (0) 2021.11.01
2. old-02  (0) 2021.10.18
1. old-01  (0) 2021.10.18
반응형

첫번째 문제는 php로 되어있었다.

 

두번째 문제는 DB 그림이 존재하는것으로 보아.

 

 

SQLi 공격을 이용해야할듯하다. (이것 또한 힌트일 수 있다.)

 

2번째 문제를 클릭해서 들어가보면 다음과 같은 문구를 볼 수 있다.

 

 

근데 이게 전부???

 

아무런 기능이 없다...

 

따라서 할 수 있는거는 페이지 소스라도 봐야한다.

 

페이지 소스를 보니 주석으로 admin.php라는 페이지가 있다고 친절하게 알려준다.

 

따라서 https://webhacking.kr/challenge/web-02/admin.php에 에 접속해보자. 

 

접속하면 다음과 같이 볼 수 있을것이다.

 

 

패스워드를 입력하라고한다.

 

여기서도 마찬가지로 페이지 소스를 보았다.

 

위의 페이지 소스를 보아하니 여기서는 딱히 얻을 힌트는 없어 보인다.

 

따라서 처음 문제에서 힌트(DB)를 얻었던것을 생각해 SQLi를 수행보았다.

 

하지만 아무런 반응을 볼 수 없었다.....

 

따라서 다시 처음으로...

 

아무리 생각해도 패스워드를 입력하는곳에서 SQLi를 수행하는것이 맞는거같은데... 하면서

 

혹시 몰라서 쿠키값을 보았다.

 

 

쿠키값을 보니 현재 timestamp가 적혀있다.

 

timestamp값을 알아보니 처음 페이지 소스보기에서 주석처리로 되었던 시간과 똑같이 적혀있었다.

 

따라서 쿠키값을 임의의 숫자인 123456789를 입력하고 다시 페이지 소스보기를 눌러보았다.

 

 

그러니 다음과 같이 페이지 소스에 시간이 반영이 되었다.

 

따라서 여기서 힌트를 얻었다. (Attack Vector)

 

힌트를 조금 알기쉽게 풀어 설명하면,

 

time이라는 쿠키값에 시간이나 어떤 값을 입력하면

 

페이지 소스보기의 시간을 출력(서버의 반응)을 볼 수 있다는 것이다.

 

여기까지의 내용으로 정리하고 가정을 세워보면 다음과 같다.

  1. 해당 문제의 그림에서 DB가 나왔기 때문에 해당 문제는 SQLi를 이용해야한다.
  2. 하지만 어디에서 SQLi가 발생하는지는 알 수 없다.
  3. 패스워드를 입력하는곳에 SQLi 공격을 수행해보았지만 딱히 별다른것이 없다.
  4. time이라는 쿠키값에 사용자가 입력한 timestamp가 반응을 보인다.
  5. time이라는 쿠키값을 가지고 SQLi를 수행해본다.

먼저 true, false 반응이 나오는지 보기 위해서

 

원래의 timestamp 뒤에 and 1and 0을 삽입하여 페이지 소스 보기 살펴보자.

 

예를들어 현재 기준으로 1634532456라는 time이 나와있을때는 참이다.

 

왜냐? 서버에서 미리 쿠키값을 설정했기때문이다.

 

따라서 1634532456 and 1을 넣었을때 이면 페이즈 소스 보기에서의 시간이 나오는 주석에는

 

2021-10-18 01:47:36가 보일것이고 1634532456 and 0,

 

false일때는 2021-10-18 01:47:36가 아닌 다른 값이 나와야한다.

 

만약 1634532456 and 0가 false인데도 2021-10-18 01:47:36가 나온다면

 

true/false를 알 수 없기 때문에 다른 방법으로 찾아봐야할것이고

 

SQLi 공격으로 푸는 문제가 아닐 수 있다.

 

그럼 time이라는 쿠키에 1634532456 and 1를 넣어보자.

 

 

예상했던거와 달리 1634532456 and 1(true)를 입력하니 2021-10-18 01:47:36가 나오지 않고

 

2070-01-01 09:00:01가 나왔다.

 

????

 

그럼 1634532456 and 0(false)인 경우일 때는 어떤지 보자.

 

 

이번에는 2070-01-01 09:00:01이 아니라 2070-01-01 09:00:00가 나왔다.

 

그럼 여기까지 정리를 해보면 true(참)일 경우에는 2070-01-01 09:00:01이 나오고 

 

false(거짓)일 경우에는 2070-01-01 09:00:00이 나온다.

 

그렇다면 time이라는 곳에 timestamp 값 말고 1=1, 1=0 true, false를 입력해도 값을까?

 

넣어보니 같았다. (Point 1)

 

따라서 이것을 기준으로 삼아 SQLi를 수행해보자. 

 

먼저 패스워드를 알려면 DB명도 알아야하고 table 명도 알아야하고 column명도 알아야 하고

 

각각의 이름을 알려면 길이 또한 알아야한다.

 

따라서 갈길이 먼것같다. 그리고 이렇게 푸는게 아직 맞는지도 모르겠다.

 

DB명을 알아내는것으로 해당 문제가 쿠키값에서 SQLi를 진행하는지

 

또한 2번 문제가 SQLi로 푸는건지 판단할 수 있을것 같다. 

 

일단 해보자.

 

일단 DB 길이를 알아내기 위해서 length(database())=DB 길이를 이용할 것이다.

 

EditThisCookie에 하나 하나 대입하는것보다 그냥 코드로 짜서 돌리는게 편할것같아 코드로 돌려버렸다.

 

코드로 DB의 길이를 알아보니 6자리임을 알 수 있었다.

 

자리수를 알아냈으니 DB명을 알아보자.

 

코드를 작성해서 알아본 결과 DB명은 다음과 같이 chall2라는것을 알 수 있다.

 

DB명을 알아냈으니 이제 테이블 구조를 살펴보자.

 

먼저 chall2이라는 DB에 몇개의 테이블이 있는지 테이블의 길이를 구해보자.

 

테이블 길이와 테이블 명, column 길이와 명을 알아내는 방법은 여기(클릭)에 가서 참고하자!

코드를 작성한 결과 0번째 테이블의 길이 즉, 첫번째 테이블은 13자리, 2번째 테이블 길이는 3개이다.

 

나머지는 1로 보아 1번째, 2번째 테이블에 우리가 원하는 패스워드 값이 있을것이라고 추측해본다.

 

따라서 먼저 첫번째 13자리의 테이블명을 알아보자.

 

13자리의 테이블명은 admin_area_pw이다.

 

여기에 우리가 원하는 패스워드가 있을것같다.

 

테이블 명을 알았으니 이제 admin_area_pw의 존재하는 column갯수길이이름을 알아야한다.

 

먼저 column의 갯수는 1개이다.

 

길이는 2자리이다. (뭔가 느낌상 column명이 pw인것 같다.)

 


역시 코드를 작성해서 실행해본 결과 컬럼명이 pw로 나왔다.

 

이제 해당 pw를 알아내면 끝이다!!

 

pw를 알아내기 위해서는 길이를 먼저 알아야한다.

 

이렇게 총 길이는 17자리이다.

 

이제 pw의 값을 알아보자!

 

코드를 작성해서 돌려본 결과 다음고 같이 pw값이 나왔다.

 

위의 pw값을 https://webhacking.kr/challenge/web-02/admin.php에 입력해보자. 

 

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

6. old-06  (0) 2021.11.03
5. old-05  (0) 2021.11.01
4. old-04  (0) 2021.11.01
3. old-03  (0) 2021.10.19
1. old-01  (0) 2021.10.18
반응형

지금 연습하고 있는 웹 해킹 사이트 Coding의 마지막 문제이다.

 

문제를 보자.

 

뭐 문제가 "spam checker"라는 문구와 함께 이상한 문자와 입에 입력하는 값이 있다.

 

그대로 복사해서 제출해보자.

 

Timeout이다...

 

뭔지 몰라 해당 페이지의 소스 코드를 보자.

 

 

중요하게 봐야할 것은 setTimeout가 있다.

 

이것을 보아.

 

2초안에 해당 페이지에서 저 값을 시간 내에 입력을 해야하나? 라는 생각이 들었다.

 

이 문제는 쉽다.. 따라서 코드는 따로 업로드 하지 않겠다.

 

저 위의 값만 2초안에 입력하면 끝이다.

 

이것으로 풀고 있던 웹 모의해킹 사이트의 coding 문제를 다 풀었다.

 

(혹시 다른 웹 해킹 사이트 Coding 문제가 있으면 댓글로 알려주시면 감사하겠습니다.)

반응형
반응형

문제를 보자.

 

 

Password를 맞추는 문제인것 같다.

 

index.phps가 아닌 해당 페이지의 소스코드를 보자.

 

 

소스코드를 보면 입력하는 pw의 최대 길이가 3이다.

 

따라서 패스워드의 총 길이는 3자리라는것이다.

 

이제 index.phps 소스 코드를 보자.

 

 

우리가 중요하게 봐야할것은 역시 php 코드이다.

 

<?php
if($_POST[pw])
{
        $password="????";

    if(!$_COOKIE[check]) exit("access denied");

    if(eregi("[^0-9]",$_POST[pw])) exit();
    if($_POST[pw]>1000) exit();

    if($_POST[pw]==???)
    {
        echo("Password is $password");
        exit();
    }

    else echo("Wrong");
}
?>

 

일단 if문에 들어가기 위해서는 pw가 무조건 어떤 값이 들어가야 한다는것이다.

 

그런 다음 if(!$_COOKIE[check]) exit("access denied");라는 코드가 있다.

 

이 코드는 뭘까? 생각을 해보았는데

 

"check"라는 쿠기값이 만약 존재하지 않는다면 exit("access denied"); 가 출력되며 프로그램이 종료된다.

 

일단 123를 입력해보자.

 

123를 입력하면 "access denied"가 뜨면서 종료된다.

 

즉,  if(!$_COOKIE[check]) exit("access denied"); 여기에 코드가 걸려 종료된다는 의미이다.

 

따라서 "check"라는 쿠키 이름을 가진 0이 아닌 값이 존재하고 값이 있어야지만  

 

if(!$_COOKIE[check]) exit("access denied");를 넘어가 프로그램이 종료되지 않는다.

 

만약 if(!$_COOKIE[check]) exit("access denied");를 통과 했다고 가정하면

 

다음 코드를 보자.

 

다음 코드는 if(eregi("[^0-9]",$_POST[pw])) exit(); 이것이다.

 

eregi()를 SQL Injection에서 설명을 따로 자세히했다.

 

eregi() 함수는 문자열을 필터링하는 함수이다.

 

[0-9]는 숫자를 의미하지만 앞에 ^가 있다.

 

^를 의미하는것은 숫자가 아닌 경우 프로그램을 종료시키겠다는것이다.

 

이것으로 보아 우리가 원하는 패스워드는 3자리 숫자이다.

 

이것을 이제 코드로 나타내보자.

 

import requests

for i in range(1,1000):
    print("시도: " + str(i))

    datas = {"pw": str(i)}
    cookie = {'check': '1'}

    get_pw = ("문제 URL")
    html = requests.post(get_pw, data=datas, cookies=cookie).text

    if "Wrong" in html:
        continue
    else:
        print(i)
        print(html)
        break

 

만약 응답 페이지에서 "Wrong"이 발견되지 않는다면 패스워드를 찾았다는 의미이므로

 

시도한 숫자 3자리가 패스워드를 의미한다.

 

프로그램에서 출력되는 패스워드를 입력하면 우리가 원하는 flag 값을 볼 수 있다.

 

반응형

'웹 해킹 > Coding' 카테고리의 다른 글

Coding 2번.  (0) 2020.12.13
Coding 1번.  (0) 2020.12.12
반응형

문제를 보면 다음과 같다.

 

 

처음에 Start라는 버튼이 있다.

 

눌러보면 다음과 같이 페이지가 변한다.

 

 

먼저 자신의 ip가 나오며 패스워드를 입력하는 폼이 있다.

 

여기서 "?"라는 버튼이 있는데 눌러보면 alert되는데 alert된 값을 보면 "1234"가 있다.

 

1234를 입력해보자.

 

 

당연히 1234가 패스워드가 아니다.

 

여기서 봐야할것은 처음에는 1/100이 였다가 1234를 입력한 후에는 2/100이 됬다.

 

위의 결과를 보고 다음과 같은 가정을 세워보면

 

2는 시도한 횟수를 의미하는것이고 100은 100번안에 우리가 원하는 패스워드가 있다는 가정이다.

 

다시한번 "?"를 클릭하여 나오는 값을 입력해보자.

 

 

위의 alert된 값을 입력하니 역시 시도횟수가 1이 증가한 3/100이 되었다.

 

이렇게 수동으로 하면 언젠가는 우리가 원하는 패스워드가 나올것이다.

 

하지만 출제자의 의도는 수동이 아닌 코드를 작성하여 위의 행위를 코드로 하고자 문제를 출제하였으니

 

코드를 작성해보자.

 

import requests

for i in range(1,시도 횟수):
    print("시도 횟수: "+str(i))
    get_pw = ("...../index.php")
    html = requests.get(get_pw).text
    if "onclick" in html:
        cur_pw = (html[html.find("onclick=alert('")+15:html.find("')>")])
        send_pw = ("...../index.php?pw="+cur_pw)
        requests.get(send_pw)
    else:
        break

 

코드 설명은 따로 하지 않겠다.

 

 

반응형

'웹 해킹 > Coding' 카테고리의 다른 글

Coding 3번.  (0) 2020.12.13
Coding 1번.  (0) 2020.12.12
반응형

이번에는 Coding 문제이다.

 

문제를 보자.

 

패스워드를 입력하는 곳이있다.

 

아무값이나 값을 입력해보자

 

당연히 password가 아니기 때문에 "Wrong password"라는 문구를 볼 수 있다.

 

소스 코드를보자.

 

 

뭐 특별한 것이없다.

 

하지만 하나 눈에 보이는것이 있다.

 

바로 "brute_force_me"라는 문구를 볼 수 있다.

 

그 뒤로 바로 sizemaxlength가 나와있다.

 

최대 길이는 4이며 사이즈는 4라는 의미이다.

 

따라서 0001에서부터 9999까지 대입을 해봐야하는것이다.

 

이것이 바로 brute force 공격기법이다.

 

brute force 공격 기법은 조합 가능한 모든 문자열을 순차적으로 하나씩 모두 대입해 보는 것이다.

 

그 야말로 무식하게 암호가 일치할때 까지 모든 경우의 수를 조합해 대입을 시도한다.

 

이론상으론 brute force 공격에 충분한 시간만 주어진다면 (모든 문자에 대한 조합을 시도해 볼 수 있기 때문에) 언젠가는 공격자가 원하는 비밀번호를 맞히게 될 것이다.

 

다만 현실적으로는 암호의 길이와 복잡도의 증가에 따라 공격에 걸리는 시간이 기하급수적으로 늘어나므로 이론상으로는 가능하나 현실적으로는 성공한다고 보기 어렵다.

 

따라서 이것을 손으로 0001~9999까지 대입하면된다.

 

하지만 문제 카테고리가 Coding이기 때문에 code로 brute force을 할것이다.

 

코드는 다음과 같다.

 

import requests

for i in range(1,10000):
    url=(".....?brute_force_me=" + str("{0:04d}".format(i)))
    res = requests.get(url)
    if "Wrong password" in res.text:
        pass
    else:
        print("Password: "+str("{0:04d}".format(i)))
        break

 

코드 설명은 따로 하지 않는다.

 

이렇게하면 우리가 원하는 패스워드를 얻을 수 있다.

 

 

반응형

'웹 해킹 > Coding' 카테고리의 다른 글

Coding 3번.  (0) 2020.12.13
Coding 2번.  (0) 2020.12.13

+ Recent posts