보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

최근들어서 버그헌팅에 빠져서 문제도 거의 못보고 블로그에 글 남길 일도 없었는데 주말에 Google CTF가 열렸길래 오랜만에 문제 구경을 좀 해봤다. 


해당 문제는 대회기간에 도저히 취약점 트리거할 포인트 자체가 안보여서 어떻게 취약점이 터지는건지 엄청 궁금했는데 대회종료 후 롸업에서 취약점 트리거되는 포인트가 엄청 신선하고 재미있어서 정리해봤다. 


문제를 보면 아래와 같이 엄청 간단한 기능 하나가 존재한다. 



도시 이름으로 검색을 하면 해당 도시에 대한 정보를 뿌려주는 형태인데 실제 요청되는 데이터는 아래와 같은 형태였다.



요청되는 파라미터 값을 보면 숫자로 이루어진걸 볼 수 있는데 해당 값은 아래 js 코드를 통해 생성된다.


post.js


function AjaxFormPost() {

  var datasend;

  var message = document.getElementById('message').value;

  message = message.toLowerCase();


  var blindvalues = [

    '10',    '120',   '140',    '1450',   '150',   '1240',  '12450',

    '1250',  '240',   '2450',   '130',    '1230',  '1340',  '13450',

    '1350',  '12340', '123450', '12350',  '2340',  '23450', '1360',

    '12360', '24560', '13460',  '134560', '13560',

  ];


  var blindmap = new Map();

  var i;

  var message_new = '';


  for (i = 0; i < blindvalues.length; i++) {

    blindmap[i + 97] = blindvalues[i];

  }


  for (i = 0; i < message.length; i++) {

    message_new += blindmap[(message[i].charCodeAt(0))];

  }


  datasend = JSON.stringify({

    'message': message_new,

  });

  var url = '/api/search';

  xhr = new XMLHttpRequest();

  xhr.open('POST', url, true);

  xhr.setRequestHeader('Content-type', 'application/json');


  xhr.onreadystatechange =

      function() {

    if (xhr.readyState == 4 && xhr.status == 200) {

        console.log(xhr.getResponseHeader('Content-Type'));

        if (xhr.getResponseHeader('Content-Type') == "application/json; charset=utf-8") {

            try {

                var json = JSON.parse(xhr.responseText);

                document.getElementById('database-data').value = json['ValueSearch'];

            }

            catch(e) {;

                document.getElementById('database-data').value = e.message;

            }

        }

        else {

            document.getElementById('database-data').value = xhr.responseText;

        }

    }

}

      xhr.send(datasend);

}


코드는 심플한데, 위 구조 상 숫자로 변환되기 전 원본 값에는 알파벳소문자밖에 들어갈 수가 없었다. 기능도 너무 심플하고 터질만한거라곤 injection류밖에 안떠올랐는데 알파벳 소문자만가지고 할만한게 뭔지 도저히 안떠올랐다. 변환된 숫자 자체가 인젝션 데이터로 들어가는건가 했는데 그것도 아니었다.

여기서 몇시간정도 고민하다가 접었는데 실제 취약점이 터지는 부분을 보니 너무 고정관념을 가지고 문제를 바라봤던 것 같다.

취약점과 같은 경우 요청 Content-type을 json type이 아닌 xml type으로 바꿔주고 데이터를 xml형태로 요청해주면 서버에서 해당 xml데이터를 파싱해주면서 xxe를 터트릴 수 있는 형태였다. 

첨엔 이런식으로 로직을 짜는 케이스가 있나? 하면서 문제를 위한 문제인가 싶은 생각도 들었는데 실제 해당 케이스로 링크드인에서 터진 리얼월드 케이스가 있어서 엄청 충격이었고 정말 웹은 하면할수록 깊이가 끝이 안보여서 재밌으면서도 힘든것 같다.

취약점과 같은 경우 아래와 같이 json형태의 데이터를 xml 형태로 변환해서 요청해주면 실제로 서버에서 해당 xml 데이터를 파싱해서 도시정보를 구해오는걸 볼 수 있었다.


이제 그냥 xxe로 플래그 찾으면 되겠다 싶었는데 간단한 문제가 있었다.


일단 요청값중에 응답에 뿌려지는 값이 없기때문에 Blind XXE를 해야했는데 External Entity는 먹히는데 아웃바운드가 다 막혀있어서 외부 요청이 안됬다. 


여기서 딱 예전에 문서에서 보고 테스트해봤던 Local DTD를 써서 Error Based Blind XXE 터트리는게 떠올랐다. 실제 실무에선 아직 못써보고 씨텝에서도 이런 환경의 XXE는 나온적이 없어서 써먹지 못하고있었는데 엄청 반가웠다.


https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/ 요글 참고해서 익스를 진행해보니 실제로 파일이 읽혔다.


Defalut Local DTD 파일과 같은 경우 Linux 환경일거라 /usr/share/yelp/dtd/docbookx.dtd 파일로 시도했고 실제로 파일이 존재하길래 이걸로 진행했다.






Flag = CTF{0x1033_75008_1004x0}




 










  



블로그 이미지

JeonYoungSin

메모 기록용 공간

,

Xs-Search

2019. 6. 6. 14:57

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

Canhack.me BBCode

2019. 6. 5. 10:50

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

Canhack.me uppercase

2019. 6. 4. 15:52

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.