glibc 2.32 부터 safe linking이라고 단일 연결 리스트의 포인터를 암호화 한다.
기본적으로 암호화와 복호화는 다음과 같다.
enc = (pointer) xor (heap_add >> 12)
pointer = (enc) xor (heap_add >> 12)
여기서 heap_add 는 해당 포인터 자기 자신의 힙 주소이다.
#include <stdlib.h>
int main(){
char* ptr1 = malloc(0x30);
char* ptr2 = malloc(0x30);
free(ptr1);
free(ptr2);
}
free 이후에 break를 걸고 메모리를 확인하면 다음과 같다.

0x4056a5 를 복호화 하면 다음과 같다.
pointer = 0x4056a5 xor 0x405 = 0x4052a0
즉, 원래 포인터는 0x4052a0임을 알 수 있다.
Bypass
하지만 key값이 독립된 값이 아니라 enc에서 추출 가능한 값이기에 encrypt된 값만 알아내면 원래의 포인터 값을 복호화 할 수 있다. (작은 크기의 청크만 사용될 경우)
아래 파이썬 코드를 통해 decrypt가 가능하다.
https://github.com/shellphish/how2heap/blob/master/glibc_2.32/decrypt_safe_linking.c
how2heap/glibc_2.32/decrypt_safe_linking.c at master · shellphish/how2heap
A repository for learning various heap exploitation techniques. - shellphish/how2heap
github.com
how2heap에 있는걸 python으로 짰다.
def decrypt(cipher):
print("복호화는 평문(앞쪽 포인터)의 첫 12비트가 알려져 있다는 사실을 사용합니다,")
print("12비트 슬라이딩 때문입니다.")
print("그리고 키, 즉 ASLR 값은 평문(앞쪽 포인터)의 앞 비트와 동일합니다.")
key = 0
plain = 0
for i in range(1, 6):
bits = 64 - 12 * i
if bits < 0:
bits = 0
plain = ((cipher ^ key) >> bits) << bits
key = plain >> 12
print(f"round {i}:")
print(f"key: {key:#018x}")
print(f"plain: {plain:#018x}")
print(f"cipher: {cipher:#018x}")
print()
return plain
# 사용 예시
cipher_text = 0x0000563835c31847 # 예시 암호문
plain_text = decrypt(cipher_text)
print(f"복호화된 평문: {plain_text:#018x}")
작은 크기의 청크만 사용되는 프로그램에서는 가능하지만 큰 청크, 정확히는 heap_addr과 pointer 값이 0x10,000 이상 차이가 나면 위 방법으로 decrypt가 불가능하다.
'포너블 > Heap' 카테고리의 다른 글
heap exploit 공부하기 좋은 곳 (1) | 2024.12.10 |
---|---|
Tcache memory leak (0) | 2024.12.10 |
Tcache House of Spirit (0) | 2024.12.10 |
breaking calloc (0) | 2024.10.12 |
__malloc_hook overwrite (0) | 2024.04.29 |
glibc 2.32 부터 safe linking이라고 단일 연결 리스트의 포인터를 암호화 한다.
기본적으로 암호화와 복호화는 다음과 같다.
enc = (pointer) xor (heap_add >> 12)
pointer = (enc) xor (heap_add >> 12)
여기서 heap_add 는 해당 포인터 자기 자신의 힙 주소이다.
#include <stdlib.h>
int main(){
char* ptr1 = malloc(0x30);
char* ptr2 = malloc(0x30);
free(ptr1);
free(ptr2);
}
free 이후에 break를 걸고 메모리를 확인하면 다음과 같다.

0x4056a5 를 복호화 하면 다음과 같다.
pointer = 0x4056a5 xor 0x405 = 0x4052a0
즉, 원래 포인터는 0x4052a0임을 알 수 있다.
Bypass
하지만 key값이 독립된 값이 아니라 enc에서 추출 가능한 값이기에 encrypt된 값만 알아내면 원래의 포인터 값을 복호화 할 수 있다. (작은 크기의 청크만 사용될 경우)
아래 파이썬 코드를 통해 decrypt가 가능하다.
https://github.com/shellphish/how2heap/blob/master/glibc_2.32/decrypt_safe_linking.c
how2heap/glibc_2.32/decrypt_safe_linking.c at master · shellphish/how2heap
A repository for learning various heap exploitation techniques. - shellphish/how2heap
github.com
how2heap에 있는걸 python으로 짰다.
def decrypt(cipher):
print("복호화는 평문(앞쪽 포인터)의 첫 12비트가 알려져 있다는 사실을 사용합니다,")
print("12비트 슬라이딩 때문입니다.")
print("그리고 키, 즉 ASLR 값은 평문(앞쪽 포인터)의 앞 비트와 동일합니다.")
key = 0
plain = 0
for i in range(1, 6):
bits = 64 - 12 * i
if bits < 0:
bits = 0
plain = ((cipher ^ key) >> bits) << bits
key = plain >> 12
print(f"round {i}:")
print(f"key: {key:#018x}")
print(f"plain: {plain:#018x}")
print(f"cipher: {cipher:#018x}")
print()
return plain
# 사용 예시
cipher_text = 0x0000563835c31847 # 예시 암호문
plain_text = decrypt(cipher_text)
print(f"복호화된 평문: {plain_text:#018x}")
작은 크기의 청크만 사용되는 프로그램에서는 가능하지만 큰 청크, 정확히는 heap_addr과 pointer 값이 0x10,000 이상 차이가 나면 위 방법으로 decrypt가 불가능하다.
'포너블 > Heap' 카테고리의 다른 글
heap exploit 공부하기 좋은 곳 (1) | 2024.12.10 |
---|---|
Tcache memory leak (0) | 2024.12.10 |
Tcache House of Spirit (0) | 2024.12.10 |
breaking calloc (0) | 2024.10.12 |
__malloc_hook overwrite (0) | 2024.04.29 |