Dork's port

FTZ Level20 Write-up(FTZ Level20 풀이) 본문

Hackerschool FTZ Write-up

FTZ Level20 Write-up(FTZ Level20 풀이)

Dork94 2018. 3. 27. 09:13

우와~~~


마! 지! 막! 문제입니다 ㅎㅎ


역시 마지막 문제 답게 시간이 엄청 걸렸네요 ㅠㅠ


문제부터 보시죠!


문제가 심상치가 않습니다! 


문제를 보니 버퍼는 80으로 선언되어있고 이번엔 fgets함수에서 길이 체크를 하는군요!


이 문제는 BOF가 아닌 FSB 즉! 포맷 스트링 버그를 이용해야 합니다.


혹시나 포맷 스트링을 모른다면 검색후에 다시 재 도전해보시길 바랍니다.



※포맷 스트링의 원리 및 해설은 자세하게 하지 않도록 하겠습니다.


이미 여러 문서에서 포맷스트링에 대한 이야기를 다루고 있으므로 저는 포맷스트링시에 몰랐던 부분에 대해 설명을 하도록하겠습니다.



정말로 시작해보죠!

우선 자연스럽게 이제 gdb를 열어 스택의 구조가 어떻게 되는지 보기 위해 열려고 했으나 main의 symbol이 존재하지 않는다고 합니다. 허허..


점점 불친절해져

그래서 ltrace를 이용하며 main의 시작 주소를 얻으려고 했으나 프롬포트가 떨어진체 아무런 반응도 없습니다.


재부팅이 필요합니다.. 시도하지마세요.


다만, 삽질의 과정에서 attackme의 파일을 copy하여 ltrace를 하면 정상적으로 open이 가능합니다.


그러나 이때 return Address를 구하려고 했으나 ASLR보안 기법이 적용되어 있어 계속 Return address의 주소를 구하지 못했습니다.


이거만 거의 하루 걸렸어요 ㅠㅠ


자 그러면 printf에 출력할 당시에 스택의 구조가 어떻게 되는지 보기 위해 출력을 해보도록 하겠습니다.


버퍼의 시작을 표시하기 위해 AAAA 및 스택을 보기 위해 %08x로 8자리씩 출력하도록 입력을 하였습니다.


포맷스트링의 기본적인 원리는 printf에서 포맷스트링을 만난 경우 Stack에서 pop을 한다는 것이 취약점으로 작용합니다.



그랬더니 AAAA출력을 제외하고 0000004f 4212ecc0 4207a759 41414141 로 printf 함수의 스택에서 buf까지의 거리는 총 12바이트 차이가 나는 것을 알 수 있습니다.


그렇다면 4번째 포맷스트링을 만날때 (pop을 4번하면) 버퍼의 시작 주소를 참조하는 값이 리턴되겠군요.


여기서 $를 이용하면 현재 스택으로 부터 n번째 스택을 참조할 수 있습니다(실제로 esp는 이동되지 않습니다. 즉, $로 참조 후 %x등과 같이 pop을 시도하면 스택의 처음부터 pop이 이루어 집니다).


%n$(format_string)


ex) $ %4$p

스택에서 부터 4번째 값을 p(주소)형식으로 출력






이때 return address를 이용할 수 없으므로 다른 개념을 이용해야합니다.


기본적으로 gcc에서는 생성자와 소멸자를 함께 컴파일 하는데 생성자는 main함수의 시작전에 실행되는 함수이고 소멸자는 main함수가 끝난 후 실행되는 함수 입니다.


ctors, dtors이라고 하며 아래와 같이 확인할 수 있습니다.




$ readelf -S ./attackme



There are 34 section headers, starting at offset 0x1dcc:


Section Headers:

  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [ 0]                   NULL            00000000 000000 000000 00      0   0  0

  [ 1] .interp           PROGBITS        080480f4 0000f4 000013 00   A  0   0  1

  [ 2] .note.ABI-tag     NOTE            08048108 000108 000020 00   A  0   0  4

  [ 3] .hash             HASH            08048128 000128 000034 04   A  4   0  4

  [ 4] .dynsym           DYNSYM          0804815c 00015c 000080 10   A  5   1  4

  [ 5] .dynstr           STRTAB          080481dc 0001dc 000061 00   A  0   0  1

  [ 6] .gnu.version      VERSYM          0804823e 00023e 000010 02   A  4   0  2

  [ 7] .gnu.version_r    VERNEED         08048250 000250 000020 00   A  5   1  4

  [ 8] .rel.dyn          REL             08048270 000270 000010 08   A  4   0  4

  [ 9] .rel.plt          REL             08048280 000280 000020 08   A  4   b  4

  [10] .init             PROGBITS        080482a0 0002a0 000017 00  AX  0   0  4

  [11] .plt              PROGBITS        080482b8 0002b8 000050 04  AX  0   0  4

  [12] .text             PROGBITS        08048308 000308 000188 00  AX  0   0  4

  [13] .fini             PROGBITS        08048490 000490 00001b 00  AX  0   0  4

  [14] .rodata           PROGBITS        080484ac 0004ac 000008 00   A  0   0  4

  [15] .eh_frame         PROGBITS        080484b4 0004b4 000004 00   A  0   0  4

  [16] .data             PROGBITS        080494b8 0004b8 00000c 00  WA  0   0  4

  [17] .dynamic          DYNAMIC         080494c4 0004c4 0000c8 08  WA  5   0  4

  [18] .ctors            PROGBITS        0804958c 00058c 000008 00  WA  0   0  4

  [19] .dtors            PROGBITS        08049594 000594 000008 00  WA  0   0  4

  [20] .jcr              PROGBITS        0804959c 00059c 000004 00  WA  0   0  4

  [21] .got              PROGBITS        080495a0 0005a0 000020 04  WA  0   0  4

  [22] .bss              NOBITS          080495c0 0005c0 000008 00  WA  0   0  4

  [23] .comment          PROGBITS        00000000 0005c0 000132 00      0   0  1

  [24] .debug_aranges    PROGBITS        00000000 0006f8 000078 00      0   0  8

  [25] .debug_pubnames   PROGBITS        00000000 000770 000025 00      0   0  1

  [26] .debug_info       PROGBITS        00000000 000795 000a84 00      0   0  1

  [27] .debug_abbrev     PROGBITS        00000000 001219 000138 00      0   0  1

  [28] .debug_line       PROGBITS        00000000 001351 00027c 00      0   0  1

  [29] .debug_frame      PROGBITS        00000000 0015d0 000014 00      0   0  4

  [30] .debug_str        PROGBITS        00000000 0015e4 0006ba 01  MS  0   0  1

  [31] .shstrtab         STRTAB          00000000 001c9e 00012b 00      0   0  1

  [32] .symtab           SYMTAB          00000000 00231c 0006f0 10     33  54  4

  [33] .strtab           STRTAB          00000000 002a0c 00042b 00      0   0  1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)




이때 .ctors 및 .dtors의 구조는 가르키는 포인터로 가면 아래와 같은 형식을 지닙니다.


시작을 알리는 0xffffffff값과 마지막을 알리는 0x00000000 사이에 실제로 생성자나 소멸자가 알맞게 들어가게 됩니다.


0xffffffff <function address> <function address>...0x00000000


그래서 우리는 main이 종료되는 시점에서 자동으로 호출되는 함수인 소멸자를 쉘코드가 있는 Address로 바꿔줄 것 이며, 이때 확인한 주소 + 4의 주소를 바꿔 줘야합니다(주소의 시작에는 0xfffffffff의 값이 있으므로).



그럼 쉘코드를 한번 작동시켜보도록 하죠!


그러면 아래와 같이 명령을 작성하실 수 있습니다.


아래 명령에 대한 부분은 포맷스트링에 대한 이해가 필요하므로 별도로 다루거나 다른 문서를 참조하시는 것을 추천드립니다.


(글이 너무 길어져요 ㅠㅠ, 제가 이해한 걸 이해시킬 자신이 없어요... ㅠ)




그러면 아래와 같이 쉘이 떨어지면서 짜라라라란 Clear!!!!!!


길고 길었던 FTZ가 끝이 났네요!



조금만 더일찍했으면 좋았을 걸 ㅠㅠ 여정을 함께하시느라 수고하셨습니다.


다음엔 LOB로 찾아뵙겠습니다!










참조 : http://bbolmin.tistory.com/34

http://blog.naver.com/PostView.nhn?blogId=haks2198&logNo=220840244540&categoryNo=0&parentCategoryNo=0&viewDate=&currentPage=1&postListTopCurrentPage=1&from=postView

http://www.hackerschool.org/HS_Boards/data/Lib_system/amadohack_fs.txt

Comments