시스템 해킹

 1576, 1/79 회원가입  로그인  
   vngkv123
   ubuntu 16.04 UAF버그..

http://www.hackerschool.org/HS_Boards/zboard.php?AllArticle=true&no=1861 [복사]


root@ubuntu:/home/ssun/system_exploit/pwn# vi uaf_vuln2.c
root@ubuntu:/home/ssun/system_exploit/pwn# gcc -fno-stack-protector -o uaf_vuln2 uaf_vuln2.c
root@ubuntu:/home/ssun/system_exploit/pwn# ./uaf_vuln2
one -> number : 0
one -> number : 12345
Address of one -> number : 0x1a89010
two -> number : 0
Address of two -> number : 0x1a89010

이게 이렇게 나오는거면 이전에 할당되었던곳으로 다시 재할당되었지만,
자동으로 초기화가 된건가요?

  Hit : 4575     Date : 2017/04/16 08:36



    
해쿨러 uaf_vuln2.c 소스코드를 올려주세요 2017/04/16  
vngkv123 #include <stdio.h>
#include <stdlib.h>

typedef struct UAF{
int number;
} uaf;

int main(){
uaf *one;
uaf *two;

one = malloc(100);
printf("one -> number : %d\n",one->number);

one->number = 12345;
printf("one -> number : %d\n", one->number);
printf("Address of one -> number : %p\n", &one->number);

free(one);
two = malloc(100);

printf("two -> number : %d\n",two->number);
printf("Address of two -> number : %p\n", &two->number);
}
root@ubuntu:/home/ssun/system_exploit/pwn#
2017/04/16  
vngkv123 지금 보기엔 free하게되면서 동시에 거기 있던 값들이 0으로 초기화되는거 같아보이는뎀 ㅠ 2017/04/16  
해쿨러 아 어려웠네요 malloc chunk는
struct malloc_chunk {

INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
우리가 malloc으로 힙할당을 요청할 때 받는 포인터는 첫 두번째꺼 이후의 주소를 받습니다. 그래서 첫 할당을 받을 때 size_t*2만큼이 페이지의 배수에서 더해진 상태로 리턴되는겁니다(32비트에서는 +8, 64비트에서는 +0x10)
이렇게 선언돼있고, number가 0으로 되는 시점은 말씀하신대로 free하는데서 존재합니다
free(one)을 할 때 one->fd = fwd 를 하는데 힙 청크 리스트에서 앞에 있는 청크가 없기 때문에 fwd는 0이고 따라서 one->fd에 0이 들어가면서, one->number가 0으로 덮어 씌워지게 되는거구요
sizeof(pointer)*2 이후에 있는 값은 덮어씌워지지않습니다(nextsize suffix가 붙은 멤버들은 large bin에서만 사용되므로)
2017/04/16  
해쿨러 root@ ~/tmp # ./test
one -> n1 : 12345
one -> n2 : 23456
one -> n2 : 34567
two -> n1 : 0
two -> n2 : 0
two -> n3 : 34567
root@ ~/tmp # cat test.c
#include <stdio.h>
#include <stdlib.h>

typedef struct UAF{
int n1;
int n2;
int n3;
} uaf;

int main(){
uaf *one;
uaf *two;

one = malloc(100);

one->n1 = 12345;
one->n2 = 23456;
one->n3 = 34567;
printf("one -> n1 : %d\n", one->n1);
printf("one -> n2 : %d\n", one->n2);
printf("one -> n2 : %d\n", one->n3);

free(one);
two = malloc(100);

printf("two -> n1 : %d\n",two->n1);
printf("two -> n2 : %d\n",two->n2);
printf("two -> n3 : %d\n",two->n3);
}
2017/04/16  
해쿨러 아 처음에 분석한내용 틀렸습니다 지금 다시 분석중이에요 2017/04/16  
해쿨러 쉽게 이해하려면 힙에서 프리를 할때 힙 청크를 관리하는 구조체를 프리된것에 맞게 변경하면서 수정된것이라고 이해하면 되구요
어렵게 이해하려면 힙에는 메모리 사이즈에 맞는 최적화를 위해 메모리 블록의 범위에 따라 다르게 처리하고 있습니다
32비트 리눅스에서는,
512바이트를 기준으로 작으면 small bin, 크면 large bin으로 처리되고, 이중에서 72바이트 미만인 블록들은 fastbin으로 따로 처리해 malloc/free 시에 오버헤드를 최소화 하도록 합니다
저도 그렇게 알고있어서 왜 100바이트를 할당받았는데 fastbin free 루틴을 타나 했는데
http://daehee87.tistory.com/478
이 글을 보니 64bit에서는 120바이트 이하의 블록을 fastbin으로 처리하더군요
결국 fastbin free루틴을 탔고 거기서는 fd만 초기화 합니다(sizeof(pointer), 즉 8바이트, sizeof(int)*2)
glibc-2.25의 malloc.c의 L3941~L3961을 보면
if (__builtin_expect (old == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
/* Check that size of fastbin chunk at the top is the same as
size of the chunk that we are adding. We can dereference OLD
only if we have the lock, otherwise it might have already been
deallocated. See use of OLD_IDX below for the actual check. */
if (have_lock && old != NULL)
old_idx = fastbin_index(chunksize(old));
p->fd = old2 = old;
}
while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);

if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))
{
errstr = "invalid fastbin entry (free)";
goto errout;
}
}
이렇게 p->fd만 변경하는 부분을 볼 수 있습니다

사이즈를 256바이트로 바꿔서 다시해보면 fastbin이 아니기 때문에 sizeof(pointer)*4가 변경되어 제가 쓴 예제코드에서 n1~n3 -> n1~n5로 했을 때 n1~n4가 변경된걸 볼 수 있습니다
2017/04/16  
해쿨러 아 64비트에서 하신줄 알았는데 32비트에서 하셨었군요 32비트에서는 sizeof(pointer) == sizeof(int)인것만 빼면 똑같습니다 2017/04/16  
해쿨러 그래서 결국은 memset(chunk, 0, sizeof(chunk)) 가 아니라
chunk->first = something 하면서 첫 4바이트만 변경된겁니다
청크 전체가 초기화된게 아닌거죠
2017/04/16  
vngkv123 감사합니당 근데 저 64비트환경에서 한거에용 2017/04/16