0x00 卷王杯-easyweb
核心代码如下
<?php
error_reporting(0);
if(isset($_GET['source'])){
highlight_file(__FILE__);
echo "\$flag_filename = 'flag'.md5(???).'php';";
die();
}
if(isset($_POST['a']) && isset($_POST['b']) && isset($_POST['c'])){
$c = $_POST['c'];
$count[++$c] = 1;
if($count[] = 1) {
$count[++$c] = 1;
print_r($count);
die();
}else{
$a = $_POST['a'];
$b = $_POST['b'];
echo new $a($b);
}
}
?>
首先需要绕过$count[]=1
,这里可以通过数组的key值溢出,设c为9223372036854775806
。而后传入a和b,通过原生类读取文件。参考:https://blog.csdn.net/qq_38154820/article/details/121112935
先可通过DirectoryIterator类来遍历目录,但只返回迭代器的第一项,利用glob协议看到假的文件
a=DirectoryIterator&b=glob://flag*
但我们知道md5加密后的字符无非是[a-z]和[0-9],在b=glob://flag*后爆破一位即可得到真实flag的文件。
最后使用SplFileObject类,读取文件内容。构造
a=SplFileObject&b=php://filter/convert.base64-encode/resource=flag56ea8b83122449e814e0fd7bfb5f220a.php
最后base64解码得到flag。
0x01 卷王杯-真·简单·不卷·现代密码签到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
n = p * q * r * s * t
e = 2
m = bytes_to_long ( os . urandom ( 500 ) + flag )
c = pow ( m , e , n )
print ( p , q , r , s , t , sep = ' \n ' )
print ( c )
'''
145332367700944303747548912160113939198078051436029477960348968315913956664143693347226702600438608693933768134575289286283267810723137895903153829001826223446477799895493265422562348917012216790077395795861238257357035152687833639085415763850743538206986781418939737511715957738982536382066693822159860701263
116660458253067608044065523310547233337730583902133756095473339390057738510707447906971188577217274861047379404014140178165569604404468897712846876108444468370709141219302291601408652742006268186059762087155933131837323952675627966299810891805398890428420575425160696531236660480933905879208166090591482794763
157931722402853245421436270609912823260313730941283152856444641969403238646482562190531038393124087232554754746464603598717356255570166081501573727336977292059427220330169044611674973569766966838498453232642731737958791706086957762244686953294662693939604300864961637325536379321027705854708492453330690705531
100973451687449518854742673778783266158999451072058606348222018797891147675959983616210003484476577612134482311993701677242007759556951494382833070563369964294544839433671087037596159753825249018950693369209927951667775267086896180395776150188902057785214767230658487267587289809918132337927575673868568976679
93960345071948255233882121683650797512129333868351496468898834736770441398743300745703393838320587998953678254272245400344928586394089488734271897540051673996675973642347859306921527430850673334243441180183460927865980713929789963587608547554858491264614271309608925634272282292964002897650355047792764365447
9144597920381774885442906257311149465702295057238600973973598305004391534618770363098565074541384771979931799878381439264848137810353858418200992191234142740194489573540381681161219332611454834544291634628456257670178843484698324641739324687497388018406214041657278323855749902661752448796122517061920880552011343608609622885787617238758769398972009949575526258430282648817039091284796330585349957724522615105102735930258969562103112238020133587096826386028128471852377225525357348919204333121695432662339443004327748973224423132988376298843862056631045488285859621661802413201793962883794915513510467912312842687601478117040419013468059983777273699192408773551806581458197324620065210523913467414181480875280203580147077789063808832356486197271376615883221558265591069223727607585313240243619515521180600435114131162272519949101464089935441251751426683447701142156416866113627126765919641034042927519834229168536331952275698122511502745177547569813354280565828372968703810158857859460406828090199683324760956105682902577189283246483314689365570862217407333103243336691401424548702387876409228977278498691200028282744239512091373110111792177228979867318546462714521296256938374618636206565791541769138267080789842400796973226733816939794717596194090232425688504890234304977612220790858557639246367437740975495450011676714198668471438814299689325208882261918460708833888406187912527346628912894921059735420931656953236560178909180587372589456926690219114173193202048332172538564489660440225377822914097420807957784201785024166011709377791129
'''
分析题目可知公钥n由多素数相乘,e=2是Rabin加密典型特征。核心原理参考:https://www.ruanx.net/rsa-solutions/ ,本题的难点在于一般rsa的Rabin算法解密都是只有2个素数,这里却有5个,对不熟悉原理的人而言很容易混乱,其实这个考点的本质是对中国剩余定理的理解。写成如下脚本:
def squareMod(c, mod): # 模意义下开根,找到 x, 使得 x^2 % mod = c
res = gmpy2.powmod(c, (mod+1)//4, mod)
return res, mod - res
def format_var(x,p,n):
Mp = n//p
return (x*Mp*gmpy2.invert(Mp, p))
def getPlaintext(x, y, z, a, b, p, q, r, s, t,n): # 假设 m%p=x, m%q=y, 求明文
res = format_var(x,p,n)+format_var(y,q,n)+format_var(z,r,n)+format_var(a,s,n)+format_var(b,t,n)
return res % n
def solve(c, p, q,r,s,t,n): # 已知 p,q,r,s,t, 解密 c
px = squareMod(c, p)
py = squareMod(c, q)
pz = squareMod(c, r)
pa = squareMod(c, s)
pb = squareMod(c, t)
for x in px:
for y in py:
for z in pz:
for a in pa:
for b in pb:
yield getPlaintext(x, y, z, a, b, p, q, r, s, t,n)
for msg in solve(c, p, q,r,s,t,n):
flag = long_to_bytes(msg)
if b'ctfshow' in flag:
print(flag)
0x02 卷王杯-犯罪高手_签到
可以取钱、查看余额、购买flag,取钱有限制,存钱需要银根。先搜索下日升昌银号,发现存在一个日昇昌密码法则。
谨防假票冒取 勿忘细视书章
堪笑世情薄 天道最公平
昧心图自私 阴谋害他人
善恶终有报 到头必分明
坐客多察看 斟酌而后行
国宝流通
顺口溜中的“谨防假票冒取 勿忘细视书章”是代表1至12个月,“堪笑世情薄,天道最公平。昧心图自私,阴谋害他人。善恶终有报,到头必分明”则是表示1至30天。“坐客多察看,斟酌而后行”是银两的1至10,“国宝流通”是万千百两。例如票号在6月10日给某省票号分号汇银3000两,其暗号代码就是“取平多宝通”。
取钱时会出现银根,猜想也是某种顺口溜,多次测试发现。
10-氏通赵
20-连通赵
30-城通赵
40-壁通赵
99-传通传
搜索关键词得到诗句,赵氏连城壁,由来天下传
,猜想“国宝流通”依旧代表万千百两。最后,存钱要达到传国传宝传流传通传
0x03 HSC-Doraemon
zip密码6位数爆破为376852
修改图片高度,可以得到一个残缺的二维码,修复下,加上定位符。
扫描得到flag
0x04 HSC-WIRESHARK
分析zip里面藏了个png,提取出来。
接着用Stegsolve提取LSB信息,发现PNG字样
保存为图片得到一个二维码,扫描结果是wrsak..iehr370
经过栅栏密码2栏解密,得到wireshark3.7.0
,就能解开压缩包了,解开的文件二进制内容中发现关键字段类似pdf,修改后缀名为pdf。
010editor分析文件格式,发现文件头残缺,补齐为255044462D312E
,最后使用wbStego4.3open工具获取隐藏信息
0x05 HSC-android
写了个很蠢的脚本,反正能跑出来
v8_1 = []
v2 = [102, 13, 99, 28, 0x7F, 55, 99, 19, 109, 1, 0x79, 58, 83, 30, 0x4F, 0, 0x40, 42]
for v4 in range(0,18):
v8_1.append(-1)
for v4 in range(0,18):
if (v4 % 2 == 0):
v8_1[v4] = v2[v4] ^ v4
for v4 in range(0,18):
if (v4 % 2 != 0):
if (v8_1[v4] != -1) :
v8_1[v4+1] = v8_1[v4] ^ v2[v4]
else:
if(v4<17 and v8_1[v4+1] != -1):
v8_1[v4] = v8_1[v4+1] ^ v2[v4]
for i in v8_1[:-1]:
print(chr(i),end='')
0x06 HSC-RSA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import gmpy2
import sympy
from Crypto.Util.number import *
flag = b '????'
z = getPrime ( 1024 )
p = sympy . nextprime ( z )
q = sympy . prevprime ( 10 * z )
n = p * q
m = bytes_to_long ( flag )
e = 0xe18e
c = pow ( m , e , n )
print ( "n=" , n )
print ( "c=" , c )
#n= 124689085077258164778068312042204623310499608479147230303784397390856552161216990480107601962337145795119702418941037207945225700624828698479201514402813520803268719496873756273737647275368178642547598433774089054609501123610487077356730853761096023439196090013976096800895454898815912067003882684415072791099101814292771752156182321690149765427100411447372302757213912836177392734921107826800451961356476403676537015635891993914259330805894806434804806828557650766890307484102711899388691574351557274537187289663586196658616258334182287445283333526057708831147791957688395960485045995002948607600604406559062549703501
#c= 57089349656454488535971268237112640808678921972499308620061475860564979797594115551952530069277022452969364212192304983697546604832633827546853055947447207342333989645243311993521374600648715233552522771885346402556591382705491510591127114201773297304492218255645659953740107015305266722841039559992219190665868501327315897172069355950699626976019934375536881746570219967192821765127789432830133383612341872295059056728626931869442945556678768428472037944494803103784312535269518166034046358978206653136483059224165128902173951760232760915861623138593103016278906012134142386906130217967052002870735327582045390117565
简单分析下这道题目,p和q之间的关系如下
p = z + a
q = 10*z - b
我们知道p、q都是加减了某个常数
10*p = 10*z + 10*a = 10*z + A
把p放大
10*p - q = A + b = k
得到10*p和q的差为一个常数
设tp = 10 * p
k**2=tp**2 - 2 * tp * q + q**2=(tp)**2 - 2 * t * n + q**2
k**2 + 4 * t *n = tp**2 + 2 * tp * q + q**2
可以通过爆破k值来计算出tp + q
,从而计算出p和q,脚本如下:
t=10
for k in range(0,1000):
x = gmpy2.iroot(k**2 + 4 * t * n,2)
if x[1]:# t*p+q是整数
p = (x[0] + k)//(2 * t)
q = t*p - k
print('p:',p,'\nq:',q)
break
计算得出p、q
1
2
p: 111664266924230584310672217327671667710935047973000520430654738129104995948600035802171323708501939460183230462999012738673733788510305174275781562493391778161104978492924899451563162871226400785486072759568388184737567195610022831797165685808940056623572151053130363074869912224709981475153891324423022575151
q: 1116642669242305843106722173276716677109350479730005204306547381291049959486000358021713237085019394601832304629990127386737337885103051742757815624933917781611049784929248994515631628712264007854860727595683881847375671956100228317971656858089400566235721510531303630748699122247099814751538913244230225750851
此时发现gmpy2.gcd(e,phi)=2
,不能直接用常规解法。
i = gmpy2.gcd(e,phi)
d = gmpy2.invert(e//i,phi)
m = pow(c,d,n)
flag = gmpy2.iroot(m,i)
if flag[1]:
print(long_to_bytes(flag[0]))
0x07 HSC-BABYRSA
求出p高位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def lfsr ( status , mask ):
out = ( status << 1 ) & 0xffffffff
i = ( status & mask ) & 0xffffffff
lastbit = 0
while i != 0 :
lastbit ^= ( i & 1 )
i = i >> 1
out ^= lastbit
return ( out , lastbit )
status = 1
mask = 0b10110001110010011100100010110101
c = list ( '0101110100100111011011011000111010000111101000101010100100100011010111011000010010100101110110011101110110010100010111001110010011101010111011001100011011010110001010011111111110100110101010101110100110011010110101110110000110010101010000010110100110110110001110101011000011110100011011100101101101001000110010100111000111001111010101011011111110010111100101111001010000100010100001000111010011011111010011101100011101011010011010110001101110110110000110010011001101100000110000110100101010010010110101100101111101110000010011101110010101110100011101100110111111001010' )
p = ''
for i in range ( 568 ):
( status , out ) = lfsr ( status , mask )
p += str ( int ( c [ i ]) ^ out )
p = int ( p , 2 )
print ( hex ( p ))
# 0x807c1395b8128e6de865ab20dd2a39684f6831464553c65215cfe2861192657b6938d227c75e902ae858fdbd8b118c8522c08a3bf978bb203bc1644fe526f2de55b065b0507958
已知p高位可以使用Coppersmith Attack方法,但需要至少576位,求出568位,差了2个十六进制数,这里需要爆破,sage代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
n = 0x4a2c6dd9af83d8cc06b4e721475e9d8a9bce1de6ddd43be7658f13bb5c5b452e9f42d9d77b8c5c3e50ef64e0edc524903e8ee759d805a63cfe613ec022115d54e73724ced3bfff73e1872b7b35b040537f8ac89523d9e2860199d6d0b1c4d7830ee5b468bd7406990ffa29caa2d8fad285b3dba209b34b427d749d7e2aebded78f49e5017bfeec1cb9f72e63506d82af561a4858f652d3fb152526c10c7e4c5e15c84803efac675fb9297d915bd1e2eda5a5de3d48bbf68380303e0d8de81704fff8c9f07ae4d15212b9066227583345425ba7a04e06fd0c16ec6bfdd764318587d1bfe76a9834043b16392018e192456cb3ea994d2a187cabfa706efbee8dbf
p = 0x807c1395b8128e6de865ab20dd2a39684f6831464553c65215cfe2861192657b6938d227c75e902ae858fdbd8b118c8522c08a3bf978bb203bc1644fe526f2de55b065b0507958
import string
dic = string . digits + "abcdef"
for a in dic :
for b in dic :
pp = hex ( p ) + a + b
#p需要用0补全到1024位
pp += '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
#要加的数字与补全p时0的个数有关
pp = int ( pp , 16 )
p_fake = pp + 0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
pbits = 1024
kbits = pbits - 576
pbar = p_fake & ( 2 ^ pbits - 2 ^ kbits )
PR .< x > = PolynomialRing ( Zmod ( n ))
f = x + pbar
try :
x0 = f . small_roots ( X = 2 ^ kbits , beta = 0.4 )[ 0 ] # find root < 2^kbits with factor >= n^0.4
p = x0 + pbar
print ( "p = " + str ( p ))
except :
pass
求出p后常规解即可。