没报名,看看题,乐子很多,利益无关,很有趣
签到:
问卷,扫码
memory
非预期,strings memory|grep flag{
重要附件
附件是流量,以及网站开docker,这里直接用神大人的knm解密就行了
看一眼,player guest可以登录
直接finallshell连接
然后进去后权限不多,基本什么都会拦截,搜了一下是rshell,直接逃逸
ssh player@123.56.74.159 -p xxxxx-t “/bin/bash”
然后测了一车提权,各种cve,各种uuid往上打都不行
这里佩服一下出题人的docker防预期水平属是牛逼,然后发现grep直接搜就搜到环境变量.jpg
非预期了属于是,问了下出题人,出题人说是平台问题.jpg
貌似预期很困难,要结合流量的udp来看。pwn掉ros2,知识盲区了。
神秘电波
四个脚本,除了第二个其他全是pye加密
搜索了一下,发现了这个
但是我发现除了pye对的上外其他都对不上,那就遂放弃对enc的分析,直接对流量分析
不过先看一下解密好的代码。
屎 山,在线工具混淆,有兴趣可以自己试试Oxyry Python Obfuscator – The most reliable python obfuscator in the world
这个混淆好在就是变量名变得恶心,但是理清逻辑还是可读的
from tutorial_interfaces .srv import FinalEncrypt #line:1
import rclpy #line:3
from rclpy .node import Node #line:4
from random import randrange #line:5
class RSA :#line:8
""#line:10
def __init__ (OOO0OOOO000O000OO ,O00OOOO0OO000O000 ,O000O000OOOOO000O =False ):#line:12
OOO0OOOO000O000OO .e =65537 #line:13
OOO0OOOO000O000OO .fast =O000O000OOOOO000O #line:14
O0O0OO0O000O00000 =0 #line:15
OOO0OOOO000O000OO .p =O00OOOO0OO000O000 #line:16
O00OO0O000O0OO00O =2 #line:17
while gcd (OOO0OOOO000O000OO .e ,O0O0OO0O000O00000 )!=1 :#line:19
O00OO0O000O0OO00O =get_random_prime (O00OOOO0OO000O000 .bit_length ())#line:20
O0O0OO0O000O00000 =lcm (O00OOOO0OO000O000 -1 ,O00OO0O000O0OO00O -1 )#line:21
OOO0OOOO000O000OO .n =O00OOOO0OO000O000 *O00OO0O000O0OO00O #line:23
OOO0OOOO000O000OO .d =invmod (OOO0OOOO000O000OO .e ,O0O0OO0O000O00000 )#line:24
if (O000O000OOOOO000O ):#line:26
OOO0OOOO000O000OO .p ,OOO0OOOO000O000OO .q =O00OOOO0OO000O000 ,O00OO0O000O0OO00O #line:27
OOO0OOOO000O000OO .d_P =OOO0OOOO000O000OO .d %(O00OOOO0OO000O000 -1 )#line:28
OOO0OOOO000O000OO .d_Q =OOO0OOOO000O000OO .d %(O00OO0O000O0OO00O -1 )#line:29
OOO0OOOO000O000OO .q_Inv =invmod (O00OO0O000O0OO00O ,O00OOOO0OO000O000 )#line:30
def encrypt (O0000OOO0OOOOOOO0 ,O00OOOO0O0OO00O00 ):#line:32
return pow (O00OOOO0O0OO00O00 ,O0000OOO0OOOOOOO0 .e ,O0000OOO0OOOOOOO0 .n )#line:33
def exgcd (O0000OO000O0OO000 ,O0000O0O0000OOO0O ):#line:35
""#line:38
O0OOO0OO0O0O00O0O ,O00000OOO0OOO0000 =1 ,0 #line:39
O000O000O0000O0O0 ,OOOO0OOOO0000000O =0 ,1 #line:40
while O0000O0O0000OOO0O :#line:41
OOO00O0O0O0OO0O0O =O0000OO000O0OO000 //O0000O0O0000OOO0O #line:42
O00000OOO0OOO0000 ,O0OOO0OO0O0O00O0O =O0OOO0OO0O0O00O0O -OOO00O0O0O0OO0O0O *O00000OOO0OOO0000 ,O00000OOO0OOO0000 #line:43
OOOO0OOOO0000000O ,O000O000O0000O0O0 =O000O000O0000O0O0 -OOO00O0O0O0OO0O0O *OOOO0OOOO0000000O ,OOOO0OOOO0000000O #line:44
O0000OO000O0OO000 ,O0000O0O0000OOO0O =O0000O0O0000OOO0O ,O0000OO000O0OO000 %O0000O0O0000OOO0O #line:45
return O0000OO000O0OO000 ,O0OOO0OO0O0O00O0O ,O000O000O0000O0O0 #line:46
def invmod (O0OOO0OOO00O00O00 ,O0O00O0OO00O00OOO ):#line:48
""#line:50
OOOOO000O0OOOO0O0 ,OOO00OO00O000O00O ,OOOO0000OOO000O00 =exgcd (O0OOO0OOO00O00O00 ,O0O00O0OO00O00OOO )#line:51
assert OOOOO000O0OOOO0O0 ==1 #line:52
if OOO00OO00O000O00O <0 :#line:56
OOO00OO00O000O00O +=O0O00O0OO00O00OOO #line:57
return OOO00OO00O000O00O #line:58
def gcd (O0OO000OOOOO0O0O0 ,O00OOOO0OOOO000O0 ):#line:60
""#line:61
while O00OOOO0OOOO000O0 :#line:62
O0OO000OOOOO0O0O0 ,O00OOOO0OOOO000O0 =O00OOOO0OOOO000O0 ,O0OO000OOOOO0O0O0 %O00OOOO0OOOO000O0 #line:63
return O0OO000OOOOO0O0O0 #line:64
def lcm (OOOO00O000O000000 ,OO0O0OO0O00000OOO ):#line:66
""#line:67
return OOOO00O000O000000 //gcd (OOOO00O000O000000 ,OO0O0OO0O00000OOO )*OO0O0OO0O00000OOO #line:68
def get_random_prime (OO0OO0O00O000O000 ):#line:70
while True :#line:71
O0O0O000O00O0O000 =get_lowlevel_prime (OO0OO0O00O000O000 )#line:72
if miller_rabin_primality_check (O0O0O000O00O0O000 ):#line:73
return O0O0O000O00O0O000 #line:74
def miller_rabin_primality_check (OOOOO00O000O0OOOO ,OO0000O000O0OOO0O =20 ):#line:76
""#line:83
if not OOOOO00O000O0OOOO >3 :#line:87
return False #line:88
if OOOOO00O000O0OOOO %2 ==0 :#line:90
return False #line:91
OOOO0000OOOO0O0OO ,O0O0OO0OOO0OOO0O0 =0 ,OOOOO00O000O0OOOO -1 #line:93
while O0O0OO0OOO0OOO0O0 %2 ==0 :#line:94
O0O0OO0OOO0OOO0O0 >>=1 #line:95
OOOO0000OOOO0O0OO +=1 #line:96
for _O00OOOO0OO00O000O in range (OO0000O000O0OOO0O ):#line:98
O000O00OO0O00O0OO =randrange (2 ,OOOOO00O000O0OOOO -1 )#line:99
OOO0O000OOOOO0O0O =pow (O000O00OO0O00O0OO ,O0O0OO0OOO0OOO0O0 ,OOOOO00O000O0OOOO )#line:100
if OOO0O000OOOOO0O0O ==1 or OOO0O000OOOOO0O0O ==OOOOO00O000O0OOOO -1 :#line:102
continue #line:103
for _O00OOOO0OO00O000O in range (OOOO0000OOOO0O0OO ):#line:105
OOO0O000OOOOO0O0O =pow (OOO0O000OOOOO0O0O ,2 ,OOOOO00O000O0OOOO )#line:106
if OOO0O000OOOOO0O0O ==OOOOO00O000O0OOOO -1 :#line:107
break #line:108
else :#line:109
return False #line:112
return True #line:115
first_50_primes =[3 ,5 ,7 ,11 ,13 ,17 ,19 ,23 ,29 ,31 ,37 ,41 ,43 ,47 ,53 ,59 ,61 ,67 ,71 ,73 ,79 ,83 ,89 ,97 ,101 ,103 ,107 ,109 ,113 ,127 ,131 ,137 ,139 ,149 ,151 ,157 ,163 ,167 ,173 ,179 ,181 ,191 ,193 ,197 ,199 ,211 ,223 ,227 ,229 ,233 ]#line:122
def get_lowlevel_prime (O0O000OO0OO0O000O ):#line:124
""#line:125
while True :#line:126
OO0OOO0O000O0O0O0 =generate_n_bit_odd (O0O000OO0OO0O000O )#line:128
for O000OOOO000OO0OOO in first_50_primes :#line:131
if OO0OOO0O000O0O0O0 %O000OOOO000OO0OOO ==0 and O000OOOO000OO0OOO **2 <=OO0OOO0O000O0O0O0 :#line:132
break #line:133
else :#line:134
return OO0OOO0O000O0O0O0 #line:137
def generate_n_bit_odd (OO000000O00O00O0O :int ):#line:139
""#line:140
assert OO000000O00O00O0O >1 #line:141
return randrange (2 **(OO000000O00O00O0O -1 )+1 ,2 **OO000000O00O00O0O ,2 )#line:142
class MinimalService (Node ):#line:145
def __init__ (OOOO0OOOO0O0O00OO ):#line:147
super ().__init__ ('minimal_service')#line:148
OOOO0OOOO0O0O00OO .srv =OOOO0OOOO0O0O00OO .create_service (FinalEncrypt ,'finalEncrypt',OOOO0OOOO0O0O00OO .finalEncrypt_callback )#line:149
def finalEncrypt_callback (O0O00000OOOO000O0 ,O00O0O00OOOOOOOOO ,OOOO000OOO00O0O00 ):#line:151
O0O00000OOOO000O0 .get_logger ().info ('Incoming request\nq: %s mode: %s'%(O00O0O00OOOOOOOOO .arg ,O00O0O00OOOOOOOOO .mode ))#line:152
OO00OOO000O00O00O =RSA (int (O00O0O00OOOOOOOOO .arg ,16 ))#line:153
O0OOOO000O000000O =0 #line:154
if O00O0O00OOOOOOOOO .mode =="onboard":#line:155
with open ("/flag","r")as O00OOO00O00O0O00O :#line:156
O00OO0O000O00OOOO =O00OOO00O00O0O00O .read ()#line:157
O00OO0O000O00OOOO =O00OO0O000O00OOOO [16 :]#line:158
else :#line:159
O00OO0O000O00OOOO ="fakeflag{LanLanLoveWangWang}"#line:160
O0OOOO000O000000O =int (O00OO0O000O00OOOO .encode ('utf-8').hex (),16 )#line:162
OO00O0OOO0O000000 =OO00OOO000O00O00O .encrypt (O0OOOO000O000000O )#line:163
OOOO000OOO00O0O00 .res1 =str (OO00OOO000O00O00O .n )#line:164
OOOO000OOO00O0O00 .res2 =str (OO00OOO000O00O00O .e )#line:165
OOOO000OOO00O0O00 .res3 =str (OO00O0OOO0O000000 )#line:166
O0O00000OOOO000O0 .get_logger ().info ('Incoming response\nn: %s e: %s c: %s'%(OOOO000OOO00O0O00 .res1 ,OOOO000OOO00O0O00 .res2 ,OOOO000OOO00O0O00 .res3 ))#line:167
return OOOO000OOO00O0O00 #line:168
def main ():#line:171
rclpy .init ()#line:172
O0O00O00O0OO0O00O =MinimalService ()#line:174
rclpy .spin (O0O00O00O0OO0O00O )#line:176
rclpy .shutdown ()#line:178
if __name__ =='__main__':#line:181
main ()
重点关注两个部分,第一个
这里定义的基本就是关于ros通信的东西,不用管
直接拉到后面,上半部分熟悉rsa的大佬应该能一眼看出来就是rsa的加密,
重点在下半部分,结合下半部分,可以分析出,首先给了key和mode,这里看流量就可以看出来
非常多,走的是RTPS协议,
是符合这里面的逻辑的
继续
重点在这,如果mode是onboard,那么他就会读取flag,
然后读取后半部分,转换为hex
然后rsa加密, 变成了后面的rsa中的c
那么前半段在哪就暂时不知道了,有了e,n,c解密还是简单的
import rsa
import gmpy2
import libnum
p = gmpy2.mpz("76206628f39d28758f043c2e014664574df923446be578b22ed19782a98d83cf", 16)
n = gmpy2.mpz("3028701244712289835084332263324003903562248750849387673673702233677338353757602651425535184296124891832944549673995130116198283557165829738446821940816673")
c = gmpy2.mpz("2316321136601071290571278358373369092106779834088698122021208036558112103589813128797337321161442043865451036052966147455929749010038563130242528643839891")
e = gmpy2.mpz(65537)
q = n // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(libnum.n2s(int(m)))
获得后半段flag,那么前半段的难点大体也可以猜测在其余的加密文件中。
这里卡了挺久,赛后问了y老师,大概知道了需要对原版项目分析,知道用的是rc4加密,这里我水平有限分析不能,直接跑
y老师的脚本好了(
from encodings import utf_8
from Crypto.Cipher import ARC4 as rc4cipher
from base64 import b64decode
with open("key", "rb") as f:
keys = f.read().split(b"\n")
with open("subscriber_member_function_enc.py", "rb") as f:
enc = f.read()[32:]
for key in keys:
raw_key = key
key = key[13:25]
print(key)
rc4 = rc4cipher.new(key)
res = rc4.decrypt(enc)
if b'rclpy' in res:
print(raw_key)
break
else:
print("Not found")
获得密钥
nJ3Ypqh61zkDxYHV
还原代码
# Copyright 2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from tutorial_interfaces.srv import FinalEncrypt
from Crypto.Cipher import AES
import rclpy,base64,json,sys,random,time
from rclpy.node import Node
from random import randrange
from std_msgs.msg import String
class MinimalSubscriber(Node):
def __init__(self):
super().__init__('wangwang')
self.subscription = self.create_subscription(
String,
'LoveRoom',
self.listener_callback,
10)
self.subscription # prevent unused variable warning
self.cli = self.create_client(FinalEncrypt, 'finalEncrypt')
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
self.req = FinalEncrypt.Request()
def listener_callback(self, msg):
self.get_logger().info('Listening: "%s"' % msg.data)
temp = json.loads(msg.data)
q = self.get_key(temp["key"],temp["mode"])
if self.miller_rabin_primality_check(int(q,16)) and temp["mode"] == 'onboard':
self.get_logger().info('0x%s is prime!' % q)
mode = ['offboard','onboard']
self.req.arg = q
self.req.mode = random.choice(mode)
response = self.cli.call_async(self.req)
else:
self.get_logger().info('0x%s is not prime!' % q)
def miller_rabin_primality_check(self, n, k=20):
'''Miller-Rabin Primality Test wwith specified round of test
Input:
n - n > 3, an odd integer to be tested for primality
k - the number of rounds of testing to perfor
Output:
True - passed (n is a strong probable prime)
False - failed (n is a composite)'''
# For a given odd integer n > 3, write n as (2^s)*d+1,
# where s and d are positive integers and d is odd.
assert n > 3
if n % 2 == 0:
return False
s, d = 0, n - 1
while d % 2 == 0:
d >>= 1
s += 1
for _ in range(k):
a = randrange(2, n - 1)
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(s):
x = pow(x, 2, n)
if x == n - 1:
break
else:
# The for loop did not encounter a break statement,
# so it fails the test, it must be a composite
return False
# Passed the test, it is a strong probable prime
return True
def get_key(self,key,mode):
plainText = ""
if mode == "onboard":
with open("/flag","r") as flagFile:
plainText = flagFile.read()
plainText = plainText[:16]
else:
plainText = "fakeflag{WangWangLoveLanLan}"
key_bytes = bytes(key, encoding='utf-8')
iv = key_bytes
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
bs = AES.block_size
length = len(plainText)
bytes_length = len(bytes(plainText, encoding='utf-8'))
padding_size = length if(bytes_length == length) else bytes_length
padding = bs - padding_size % bs
padding_text = chr(padding) * padding
content_padding = plainText + padding_text
encrypt_bytes = cipher.encrypt(bytes(content_padding, encoding='utf-8'))
# result = str(encrypt_bytes, encoding='utf-8')
# by = bytes(result, 'UTF-8')
hexstring = encrypt_bytes.hex()
return hexstring
def main(args=None):
rclpy.init(args=args)
minimal_subscriber = MinimalSubscriber()
rclpy.spin(minimal_subscriber)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
最后可以得知,前半部分就是aes解密c,那最后直接aes跑一下就出来了。
这里key的话是在发送aes这几个参数的前面,他们是一组流量包
最后解密即可
获得整体flag