-
-
[推荐]2019 KCTF 总决赛 | 第二题《南充茶坊》点评及解题思路
-
发表于: 2019-12-11 18:51 4014
-
第二道题《南充茶坊》历时3天,已于5号中午12点关闭攻击通道。共有2548人围观,最终只有4支团队攻破成功。7HxzZ战队率先攻破此题,一共用时约28小时,可见此题难度很大,Acehub战队和大众代表队的readyu攻势迅猛,金左手战队也迎头赶上,与前几位选手相差不大。
1、题目简介
"鸡米花系统第一级开启"我一睁眼,只见一片塞外风光,往来行人车马络绎,似是个边疆小镇。起身差点被自己绊倒,才发现竟已换上了缎袍,腰间还有一把折扇("唰"),我成了……古代文人……?"阁下真是好运气,初次来到就碰上这么个简单关卡。边塞风光可是壮美,只是风沙些许大,阁下不妨先去茶馆避避吧。"视线扫过四周,我终于看清身后兀然竟立着一座茶馆,风裹着大漠的气息,将门外的旌旗吹得漫卷飞扬,旗上四个大字"南充茶坊"。一头雾水,进去探探再说。将将落座,却见一六七岁样貌孩童嬉笑着向我跑来。"大哥哥,刚才有个仙女姐姐来,让我把谜题交给一个摇扇子的大哥哥,应该就是大哥哥你啦!"又有仙女儿,又有谜题,甚好甚好。"不过,大哥哥要陪我玩成语接龙,我才把谜题给你。"成语接龙……?也行也行,为了拿到仙女姐姐的谜题,哥哥就陪你玩儿会。"既然在我们家的茶馆,就以'茶'字开头吧。大哥哥先来。"茶余饭后→后来居上→上善若水→水落石出→出其不意→意气用事→事在人为→为所欲为//众所周知,为所欲为是成语接龙自动机的接受状态,那我换一个事不关己→己所不欲→欲加之罪→罪孽深重→重义轻利→利诱威逼→逼上梁山→山穷水尽→尽力而为→为所欲为//emmmmmm,那我再换一个尽心尽力→力大无穷→穷兵黩武→武偃文修→修身养性→性命攸关→关怀备至→至高无上→上下一心→心猿意马→马首欲东→东遮西掩→掩口卢胡→胡作非为→为所欲为//怎么又回来了,重来重来东门黄犬→犬牙差互→互通有无→无可奈何→何乐不为→为所欲为//……事出有因→因小失大→大智若愚→愚公移山→山穷水尽→尽力而为→为所欲为//……显然,这个小朋友是'有备而来',没准仙女姐姐正躲在哪里看着我们偷笑呢。怎样才能挽回局面呢……?

2、看雪评委crownless点评
这道题考查的范围较为广泛,需要参赛者对密码学、PE文件结构、python反编译等都有一定程度的理解,题目质量较高。因此,攻破此题的参赛者数量也不多。此外,题目writeup的质量较高,值得一看。
3、出题团队简介

4th拜各位大佬!
本次2019KCTF决赛的中娅之戒由4名队员组成:
iiiiiiiiiiii:一个小肥宅,晚上不睡觉,白天起不来
xh1998:国家一级彩蛋制造专家
leafpad:仍旧是萌新
Venessa:深深觉得还是自然科学有意思的密码学方向在读研(小)究(姑)生(娘)
# 我们的征程是吸尘器和云台
# 两只黄鹂鸣翠柳 一行白鹭上青天
4、设计思路
5、解题思路

试运行

初步分析
1 | v3 = fopen( "CM.exe" , "rb" );<br>v4 = calloc( 0x28131ui64 , 1ui64 );<br>fseek(v3, 0x8000 , 0 );<br>fread(v4, 1ui64 , 0x28131ui64 , v3);<br>LODWORD(v3) = aPsafe_get_orig_size(v4);<br>v5 = calloc((unsigned int )v3, 1ui64 );<br>aPsafe_depack(v4, 0x28131i64 , v5, (unsigned int )v3); |

1 | [ * ] Processingcm.exe<br>[ * ] Error : Unsupported pyinstallerversion or nota pyinstaller archive |
1 | def checkFile( self ):<br> print ( '[*] Processing {0}' . format ( self .filePath))<br> # Check if it is a 2.0 archive<br> self.fPtr.seek(self.fileSize -self.PYINST20_COOKIE_SIZE, os.SEEK_SET)<br> magicFromFile =self.fPtr.read(len(self.MAGIC))<br> <br> if magicFromFile ==self.MAGIC:<br> self.pyinstVer =20 # pyinstaller 2.0<br> print('[*] Pyinstaller version: 2.0')<br> return True<br> <br> # Check for pyinstaller 2.1+ before bailing out<br> self.fPtr.seek(self.fileSize -self.PYINST21_COOKIE_SIZE, os.SEEK_SET)<br> magicFromFile =self.fPtr.read(len(self.MAGIC))<br> <br> if magicFromFile ==self.MAGIC:<br> print('[*] Pyinstaller version: 2.1+')<br> self.pyinstVer =21 # pyinstaller 2.1+<br> return True<br> <br> print('[*] Error : Unsupported pyinstaller version or not a pyinstaller archive')<br> return False |

py代码反编及分析
1 | #CM.py<br>from CMpubimport pub_n_list, pub_e_list<br>from generalimport valid_serial, valid_username, get_enc_seq, enc0<br>from Crypto.Util.numberimport bytes_to_long<br>from osimport system<br> <br>def check(serial, seq):<br> now = serial<br> for jin range(len(seq)):<br> i = seq[j]<br> n = pub_n_list[i]<br> e = pub_e_list[i]<br> if now > pub_n_list[i]:<br> return False<br> if i >0:<br> now = pow(now, e, n)<br> else:<br> now = enc0(now, e, n)<br> if 0 == now:<br> return False<br> <br> return now<br> <br> <br>def main():<br> username0 = input('Please input Username:\t')<br> serial0 = input('Please input Serial:\t')<br> try:<br> valid_serial(serial0)<br> valid_username(username0)<br> except:<br> print('\nInvalid Input\n')<br> return<br> else:<br> serial = int(serial0,16)<br> username = username0.encode('utf-8')<br> seq = get_enc_seq(username)<br> username = bytes_to_long(username)<br> check_value = check(serial, seq)<br> if check_value == username:<br> print('\nCorrect!\n')<br> else:<br> print('\nOops! The encrypted Serial is not "' + username0 +'".\n')<br> system('pause')<br> <br> <br>if __name__ =='__main__':<br> main() |
1 | #general.py<br>from hashlibimport sha256<br> <br>def valid_serial(serial):<br> assert type(serial) == str<br> assert len(serial) >0<br> if not '0' != serial[0]:<br> assert 1 == len(serial)<br> for cin serial:<br> if not ord('0') <= ord(c) <= ord('9'):<br> if not ord('A') <= ord(c) <= ord('F'):<br> raise AssertionError<br> <br> return serial<br> <br> <br>def valid_username(username):<br> assert type(username) == str<br> assert len(username) >0<br> for cin username:<br> assert ord(c) >0<br> <br> return username<br> <br> <br>def get_enc_seq(username):<br> h = sha256()<br> h.update(username)<br> hash_value = int(h.hexdigest(),16)<br> T =9<br> S =10<br> stat = [0] * (T +1)<br> seq = [0]<br> n = hash_value<br> while n >0:<br> if S * T > len(seq):<br> now = n % T +1<br> if stat[now] < S:<br> seq.append(now)<br> stat[now] +=1<br> n //= T<br> <br> return seq<br> <br> <br>def enc0(m, e, n):<br> nbit =192<br> T = (n.bit_length() -1) // nbit +1<br> ans =0<br> for iin range(T -1,-1,-1):<br> <br> def xxx(x, t):<br> return x >> t * nbit & (1 << nbit) -1<br> <br> now_n = xxx(n, i)<br> now_e = xxx(e, i)<br> now_m = xxx(m, i)<br> if now_m >= now_n:<br> return 0<br> ans = (ans << nbit) + pow(now_m, now_e, now_n)<br> <br> return ans |
- 检查用户名及序列号格式
- 用户名编码成0开头的其余皆为1-9的数字序列
- 通过数字序列和序列号计算对应的用户名
- 利用计算出的用户名校验输入用户名

- 试除法(Trial division)
- 轮式因子分解法(Wheel factorization)
- Pollard's rho算法(Pollard's rho algorithm)
- 代数群因子分解算法(Algebraic-group factorisation algorithms),包括:
- Pollard's p-1算法(Pollard'sp−1 algorithm)
- Williams' p+1算法(Williams'p+1 algorithm)
- Lenstra椭圆曲线因子分解法(Lenstra elliptic curve factorization)
- 费尔马因子分解法(Fermat's factorization method)
- 欧拉因子分解法(Euler's factorization method)
- 特殊数域筛选法(Special number field sieve, SNFS)
- Dixon's算法(Dixon's algorithm)
- 连分数因子分解法(Continued fraction factorization, CFRAC)
- 二次筛选法(Quadratic sieve)
- 自然筛选法(Rational sieve)
- 普通数域筛选法(General number field sieve, GNFS)
- 二次剩余因子分解法(Shanks' square forms factorization, SQUFOF)
- Wiener’s attack
- 模数公约数
1 | def de_cofactor():<br> l = [ 3 , 4 , 5 , 6 , 7 , 8 , 9 ]<br> for idx1in l[: - 1 ]:<br> tmp = l.index(idx1)<br> for idx2in l[tmp + 1 :]:<br> n1 = pub_n_list[idx1]<br> n2 = pub_n_list[idx2]<br> fa = gcd(n1,n2)<br> if fa ! = 1 :<br> print ( '{}-{}:{}' . format (idx1,idx2,fa))<br> break |
1 | def de_fmt():<br> for idxin [ 3 , 4 , 5 , 6 , 9 ]:<br> n = pub_n_list[idx]<br> n1,flag = iroot(n, 2 )<br> for iin range ( 1 , 100000000 ): #4:30766579<br> d = (n1+i)**2 - n<br> d1,d1_flag = iroot(d,2)<br> if d1_flag:<br> print('{}:{} * {}'.format(idx,n1+i-d1,n1+i+d1))<br> break |
1 | <br> def de_nenc():<br> for idxin [ 3 , 5 ]:<br> c = 1234 <br> c = pow (c,pub_e_list[idx],pub_n_list[idx])<br> for iin range ( 1 , 3000 ): #5:972<br> c = pow(c,pub_e_list[idx],pub_n_list[idx])<br> if c ==1234:<br> print(idx,i)<br> break |

1 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 yafu<br> 3012613441 <br> 408348887278831461892610983390456945715612189153595698014201723503582993177671598068402822885301113712860820610804438839912419021850712894809826330539771179969086301239790288951959814006579380835620367785931463468740342519 <br> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 yafu<br> 33432251606513919330224622231317366537583494828490610118526091564149071313914670507018535045422242143860852944567717 <br> 36796059015915981995743432427927902530578158826788662547913509800095517347118062478922356654842254755916234587213077 <br> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3 msieve<br> 35649202521608097930406469243867007880953508969230221480710213863570107942900924156757677983779306674419762868555881 <br> 34508357350889186927005771176009938286558270433215942821530479444470024241863567490529149148811494601012847563892119 <br> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 4 root<br> 35073848198058968264749790689745197012385911613318747771531468291963621262770461817935405298684967674641247961569207 <br> 35073848198058968264749790689745197012385911613318747864444430571453140176920442860760331340059929469925220710756489 <br> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 mutli enc 927 <br> <br> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6 yafu<br> 26153324138194530872309557862464074525306735281010877795840958405693838446634362256045094682633541743845611502925341 <br> 47037312003475104493787837223043052957997925611039410248119470723357078273788416725476266555402464778755414193496517 <br> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 7 gcd with 8 <br> 31550999035553713541658090861648847436644679578302163888858659624252927748982782365539915958668921514993162014394013 <br> 38990070212574870147996976234485159783294770114760127229326637585796111190341078784636949590143174262955343019982189 <br> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 8 gcd with 7 <br> 31550999035553713541658090861648847436644679578302163888858659624252927748982782365539915958668921514993162014394013 <br> 38990721481148651230245403302870059427845712033679000902896256935166030973974325392836914596772977488693063112507763 <br> <br> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 9 factordb<br> 33478071698956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489 <br> 36746043666799590428244633799627952632279158164343087642676032283815739666511279233373417143396810270092798736308917 |
计算序列号
1 | d_list = [ 0 ,<br> 1229061379053482744823152709690434888605435342198642793057361495153090878523561729157374272032362247153476832905266577597936263875922635915871109425948662301015976854125739243408750974498511602088546684816889116185016023460221241669 ,<br> 777757116197429014363243483536399809327489478672765799132008796613164652485176625704586327714092043960999487185568710834915265226900792279774759093092549483626423808502139519027474793822444064402879491357824424416255246538629615833 ,<br> 823517544270505260875748293850542665969704501523967290816451493771583601768953397655610971102989198539746963950912153425409736040224095605717482218948794082459357645210952999432155985335398508720958815348576453854403675948095665769 ,<br> 201419093613711761798540120588879041236840801068737686325195769604622639638393093247553615834885873886104026065263583851481439063609372020322699902282368593094548575688163522138303256348801329176798576662806247524508598547117027909 ,<br> 0 ,<br> 922636550562204599680939922372659456090989853033213781402035071733018496656291847983487357218691366381484789513851952285401356211108779700672241308549478980097499945532484886375918716118514969665502334756552044884230174523807259329 ,<br> 627567627354326605515830278366021138159090868181225778581524414114680017539982571880336309158282300554534438008003322761037932916796881232969829716072515736525821134512405811777096389132413642826921797756179543381108933388795804769 ,<br> 87763698610259057934268676955169084425464093902524300758830437447338976666426976224860384022223934601405128988601223084334832120115000652985202766336425151725676233383146800117094745887838499800523801384784235875984207816521259103 ,<br> 476133969086427941368462698171675198835788633664060460402567958612344234273279077792246479357026462298849835811290909092037603283477920353500979549470973082158764663458247740861805650589884280458476962517272738918503674157481390819 ]<br> <br> <br> def main(): <br> username0 = input ( 'Please input Username:\t' )<br> # serial0 = input('Please input Serial:\t')<br> try:<br># valid_serial(serial0)<br> valid_username(username0)<br> except:<br> print('\nInvalid Input\n')<br> return<br> else:<br># serial = int(serial0, 16)<br> username = username0.encode('utf-8')<br> seq = get_enc_seq(username)<br> username = bytes_to_long(username)<br> serial = gen_code(username,seq)<br> check_value = check(serial, seq)<br> if check_value == username:<br> print('\nCorrect!\n')<br> print('Username:'+username0)<br> print('Serial:'+long_to_bytes(serial).hex().upper())<br> else:<br> print('\nOops! The encrypted Serial is not "' + username0 +'".\n')<br> system('pause')<br> <br> <br>def gen_code(name,seq):<br> now = name<br> for jin range(len(seq)-1,-1,-1):<br> i = seq[j]<br> n = pub_n_list[i]<br> d = d_list[i]<br> e = pub_e_list[i]<br> if now > pub_n_list[i]:<br> return False<br> if i ==5:<br> for _in range(972):<br> now = pow(now, e, n)<br> elif i >0:<br> now = pow(now, d, n)<br> else:<br> now = dec0(now, e, n)<br> return now<br> <br>def dec0(m, e, n):<br> nbit =192<br> T = (n.bit_length() -1) // nbit +1<br> ans =0<br> now_d = [3278088227273880484685188900446423165326324359156241482897,<br> 5595268830569065289149075246423557309146176813871482913465,<br> 5168422726700129702446055556002687483861807983023239862033,<br> 935387432279493795970516178982877288303534264234974679937,<br> ]<br> now_n = [6277052177355867862708971469565390229110991711310599757597,<br> 6277031389885428580574088212602983156224270664268963309877,<br> 6277087998938792861042405062580257456598525084583043892593,<br> 4973828634074084070222279013150769733596223651485114564273,<br> ]<br> for iin range(T -1,-1,-1):<br> def xxx(x, t):<br> return x >> t * nbit & (1 << nbit) -1<br> now_m = xxx(m, i)<br> tmp = pow(now_m, now_d[i], now_n[i])<br> ans = (ans << nbit) + tmp<br> return ans |
似乎用户名计算出来的数字序列中5比较多,计算稍需要点时间:

END
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课