Off by one overflow

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

기본 정보

단지 1바이트 오버플로우에 접근할 수 있다는 것은 공격자가 다음 청크의 size 필드를 변경할 수 있음을 의미합니다. 이는 실제로 어떤 청크들이 free되는지를 조작할 수 있게 하며, 잠재적으로 다른 합법적인 청크를 포함하는 청크를 생성할 수 있습니다. 익스플로잇 방법은 double free 또는 overlapping chunks와 유사합니다.

There are 2 types of off by one vulnerabilities:

  • Arbitrary byte: 이 유형은 해당 바이트를 임의의 값으로 덮어쓸 수 있게 합니다
  • Null byte (off-by-null): 이 유형은 해당 바이트를 오직 0x00으로만 덮어쓸 수 있게 합니다
  • A common example of this vulnerability can be seen in the following code where the behavior of strlen and strcpy is inconsistent, which allows set a 0x00 byte in the beginning of the next chunk.
  • This can be expoited with the House of Einherjar.
  • If using Tcache, this can be leveraged to a double free situation.
Off-by-null ```c // From https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/off_by_one/ int main(void) { char buffer[40]=""; void *chunk1; chunk1 = malloc(24); puts("Get Input"); gets(buffer); if(strlen(buffer)==24) { strcpy(chunk1,buffer); } return 0; } ```

다른 검사들과 함께, 이제 chunk가 free될 때마다 이전 previous size가 metadata의 chunk에 설정된 size와 비교되므로 이 공격은 2.28 버전부터 상당히 복잡해졌다.

Code example:

Goal

  • 하나의 chunk가 다른 chunk 내부에 포함되도록 만들어, 두 번째 chunk에 대한 쓰기 접근으로 내부에 포함된 chunk를 덮어쓸 수 있게 한다

Requirements

  • size metadata 정보를 변경할 수 있는 Off by one overflow

General off-by-one attack

  • 세 개의 chunk A, BC를 할당한다(예: 크기 0x20). 그리고 top-chunk와의 consolidation을 방지하기 위해 추가 하나를 더 할당한다.
  • C를 free한다(0x20 Tcache free-list에 삽입됨).
  • A를 사용해 B에 overflow를 발생시킨다. off-by-one을 악용해 Bsize 필드를 0x21에서 0x41로 변경한다.
  • 이제 B는 free된 chunk C를 포함하게 된다.
  • B를 free하고 0x40 크기의 chunk를 할당하면(해당 위치에 다시 배치된다).
  • C는 여전히 free 상태이므로 그 안의 fd 포인터를 수정할 수 있다 (Tcache poisoning)

Off-by-null attack

  • 연속으로 3개의 메모리 chunk(a, b, c)를 할당한 뒤 중간 chunk를 free한다. 첫 번째 chunk에는 off by one overflow 취약점이 있고 공격자는 이를 0x00으로 악용한다(이전 바이트가 0x10이었다면 중간 chunk가 실제보다 0x10 작다고 표시하게 된다).
  • 그 다음 중간에 free된 chunk(b) 안에 더 작은 두 개의 chunk를 추가로 할당한다. 그러나 b + b->size가 가리키는 주소가 실제보다 작기 때문에 c chunk는 업데이트되지 않는다.
  • 그 후 b1과 c가 free된다. c - c->prev_size가 여전히 b(현재 b1)를 가리키므로 두 개는 하나의 chunk로 병합된다. 하지만 b2는 b1과 c 사이에 여전히 남아 있다.
  • 마지막으로 새로 malloc을 수행하면 실제로 b2를 포함하는 이 메모리 영역을 회수하게 되어, 새 malloc의 소유자가 b2의 내용을 제어할 수 있게 된다.

This image explains perfectly the attack:

https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks

Modern glibc hardening & bypass notes (>=2.32)

  • Safe-Linking은 이제 모든 singly linked bin 포인터를 fd = ptr ^ (chunk_addr >> 12)로 저장해 보호하므로, size의 하위 바이트만 뒤집는 off-by-one은 보통 Tcache poisoning이 동작하기 전에 XOR 마스크를 재계산하기 위한 heap leak이 필요하다.
  • 실제로 leak 없이 쓸 수 있는 트릭은 포인터를 “double-protect“하는 것이다: 이미 제어하는 포인터를 PROTECT_PTR로 인코딩한 뒤, 같은 기법을 재사용해 위조한 포인터를 인코딩하면 정렬 검사(alignment check)가 새로운 주소를 노출하지 않고도 통과한다.
  • safe-linking + single-byte corruptions 작업 흐름:
  1. victim chunk를 제어하는 이미 가진 free된 chunk를 완전히 덮을 때까지 확장한다 (overlapping-chunk 설정).
  2. heap 포인터(stdout, UAF, 부분적으로 제어된 struct 등)를 leak하고 키 heap_base >> 12를 도출한다.
  3. free-list 포인터를 쓰기 전에 재인코딩한다 — 인코딩된 값을 user data 안에 배치하고 single-byte 쓰기만 가능한 경우 나중에 memcpy로 옮긴다.
  4. 위조 포인터가 올바르게 인코딩되면 Tcache bin attacks와 결합해 할당을 __free_hook이나 tcache_perthread_struct 엔트리로 리다이렉트한다.

최신 익스플로잇을 디버깅할 때 인코드/디코드 단계를 연습하기 위한 최소한의 헬퍼:

def protect(ptr, chunk_addr):
return ptr ^ (chunk_addr >> 12)

def reveal(encoded, chunk_addr):
return encoded ^ (chunk_addr >> 12)

chunk = 0x55555555c2c0
encoded_fd = protect(0xdeadbeefcaf0, chunk)
print(hex(reveal(encoded_fd, chunk)))  # 0xdeadbeefcaf0

최근 실제 표적: glibc __vsyslog_internal off-by-one (CVE-2023-6779)

  • 2024년 1월 Qualys는 CVE-2023-6779을 상세히 설명했는데, 이는 __vsyslog_internal() 내부의 off-by-one 취약점으로 syslog()/vsyslog()의 포맷 문자열이 INT_MAX를 초과할 때 발생하여 종료 \0이 glibc 2.37–2.39 시스템에서 인접 청크의 가장 하위 size 바이트를 손상시킨다 (Qualys advisory).
  • 그들의 Fedora 38 exploit pipeline:
  1. 과도하게 긴 openlog() ident을 만들어 vasprintf가 공격자가 제어하는 데이터 옆에 힙 버퍼를 반환하게 한다.
  2. 이웃 청크의 size | prev_inuse 바이트를 파괴하기 위해 syslog()를 호출하고, 해당 청크를 free한 뒤 공격자 데이터와 중첩되도록 consolidation을 강제한다.
  3. 중첩된 뷰를 이용해 tcache_perthread_struct 메타데이터를 손상시키고 다음 할당이 __free_hook를 가리키게 만든 다음 system 또는 root 권한을 위한 one_gadget으로 덮어쓴다.
  • 테스트 하니스에서 손상 쓰기를 재현하려면, 거대한 argv[0]으로 fork하고 openlog(NULL, LOG_PID, LOG_USER)를 호출한 다음 syslog(LOG_INFO, "%s", payload)를 호출하라. 여기서 payload = b"A" * 0x7fffffff; pwndbgheap bins는 즉시 단일 바이트 덮어쓰기를 보여준다.
  • Ubuntu는 이 버그를 CVE-2023-6779로 추적하며, 이 동일한 INT truncation이 이 취약점을 신뢰할 수 있는 off-by-one primitive로 만든다고 문서화한다.

기타 예제 및 참고자료

  • https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks
  • Bon-nie-appetit. HTB Cyber Apocalypse CTF 2022
  • Off-by-one은 strlen이 다음 청크의 size 필드를 고려하기 때문에 발생한다.
  • Tcache가 사용되므로 일반적인 off-by-one 공격으로 Tcache poisoning을 통해 arbitrary write primitive를 얻을 수 있다.
  • Asis CTF 2016 b00ks
  • 문자열 끝의 0x00 바이트가 다음 필드에 의해 덮어써지기 때문에 off-by-one을 악용하여 힙에서 주소를 leak하는 것이 가능하다.
  • Arbitrary write는 off by one 쓰기를 악용해 포인터가 가짜 포인터를 가진 가짜 struct가 만들어질 다른 위치를 가리키게 함으로써 얻는다. 그 다음 이 struct의 포인터를 따라가면 arbitrary write를 얻을 수 있다.
  • 힙이 mmap으로 확장되면 mmap이 할당하는 메모리는 libc로부터 고정된 오프셋을 가지므로 libc 주소가 leak된다.
  • 마지막으로 arbitrary write를 악용해 __free_hook 주소에 one gadget을 써넣는다.
  • plaidctf 2015 plaiddb
  • 사용자 입력 라인을 읽는 getline 함수에 NULL off by one 취약점이 있다. 이 함수는 콘텐츠의 “key“를 읽는 데 사용되고 콘텐츠 자체를 읽는 데는 사용되지 않는다.
  • In the writeup 초기 5개의 청크가 생성된다:
  • chunk1 (0x200)
  • chunk2 (0x50)
  • chunk5 (0x68)
  • chunk3 (0x1f8)
  • chunk4 (0xf0)
  • chunk defense (0x400)는 top chunk와 consolidating되는 것을 피하기 위한 것이다
  • 그다음 chunk 1, 5, 3이 free되어, 다음과 같이 된다:

[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]

- 그 후 chunk3 (0x1f8)을 악용하여 null off-by-one을 이용해 prev_size를 `0x4e0`으로 쓴다.
- 초기에 할당된 chunks 1, 2, 5, 3의 크기와 그들 청크 4개의 헤더 크기 합이 `0x4e0`인 것을 주목하라: `hex(0x1f8 + 0x10 + 0x68 + 0x10 + 0x50 + 0x10 + 0x200) = 0x4e0`
- 그 다음 chunk 4가 free되어 시작지점까지 모든 청크를 소비하는 청크가 생성된다:
- ```python
[ 0x4e0 Chunk 1-2-5-3 (free) ] [ 0xf0 Chunk 4 (corrupted) ] [ 0x400 Chunk defense ]

[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]

- 그 후 원래의 chunk 1을 채우기 위해 `0x200` 바이트를 할당한다
- 그리고 또 다른 0x200 바이트가 할당되어 chunk2가 파괴되므로, 따라서 fucking leak가 없어서 동작하지 않는다? 아마 이건 하면 안 될 것이다
- 그 다음 0x58개의 "a"로 다른 청크를 할당( chunk2를 덮어쓰고 chunk5에 도달)하고 chunk5의 fast bin 청크의 `fd`를 `__malloc_hook`를 가리키도록 수정한다
- 그 다음 0x68 크기의 청크를 할당하여 `__malloc_hook`에 있는 가짜 fast bin 청크가 다음 fast bin 청크가 되게 한다
- 마지막으로 새로운 0x68 fast bin 청크가 할당되고 `__malloc_hook`가 `one_gadget` 주소로 덮어써진다

## References

- [Qualys Security Advisory – CVE-2023-6246/6779/6780](https://www.qualys.com/2024/01/30/cve-2023-6246/syslog.txt)
- [Ubuntu Security – CVE-2023-6779](https://ubuntu.com/security/CVE-2023-6779)
- [Breaking Safe-Linking in Modern Glibc – Google CTF 2022 "saas" analysis](https://blog.csdn.net/2402_86373248/article/details/148717274)

> [!TIP]
> AWS 해킹 배우기 및 연습하기:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> GCP 해킹 배우기 및 연습하기: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Azure 해킹 배우기 및 연습하기: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>HackTricks 지원하기</summary>
>
> - [**구독 계획**](https://github.com/sponsors/carlospolop) 확인하기!
> - **💬 [**디스코드 그룹**](https://discord.gg/hRep4RUj7f) 또는 [**텔레그램 그룹**](https://t.me/peass)에 참여하거나 **트위터** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**를 팔로우하세요.**
> - **[**HackTricks**](https://github.com/carlospolop/hacktricks) 및 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.**
>
> </details>