2022红明谷ctf MISC writeUP

没报名,看看题,乐子很多,利益无关,很有趣

签到:

问卷,扫码

memory

非预期,strings memory|grep flag{

重要附件

附件是流量,以及网站开docker,这里直接用神大人的knm解密就行了

图片[1]-2022红明谷ctf MISC writeUP-魔法少女雪殇

看一眼,player guest可以登录

直接finallshell连接

图片[2]-2022红明谷ctf MISC writeUP-魔法少女雪殇

然后进去后权限不多,基本什么都会拦截,搜了一下是rshell,直接逃逸

ssh player@123.56.74.159 -p xxxxx-t “/bin/bash”

然后测了一车提权,各种cve,各种uuid往上打都不行

这里佩服一下出题人的docker防预期水平属是牛逼,然后发现grep直接搜就搜到环境变量.jpg

图片[3]-2022红明谷ctf MISC writeUP-魔法少女雪殇

非预期了属于是,问了下出题人,出题人说是平台问题.jpg

貌似预期很困难,要结合流量的udp来看。pwn掉ros2,知识盲区了。

神秘电波

图片[4]-2022红明谷ctf MISC writeUP-魔法少女雪殇


四个脚本,除了第二个其他全是pye加密

搜索了一下,发现了这个

Falldog/pyconcrete: Protect your python script, encrypt it as .pye and decrypt when import it (github.com)

但是我发现除了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 ()

重点关注两个部分,第一个

图片[5]-2022红明谷ctf MISC writeUP-魔法少女雪殇

这里定义的基本就是关于ros通信的东西,不用管

直接拉到后面,上半部分熟悉rsa的大佬应该能一眼看出来就是rsa的加密,

图片[6]-2022红明谷ctf MISC writeUP-魔法少女雪殇
图片[7]-2022红明谷ctf MISC writeUP-魔法少女雪殇

重点在下半部分,结合下半部分,可以分析出,首先给了key和mode,这里看流量就可以看出来

图片[8]-2022红明谷ctf MISC writeUP-魔法少女雪殇

非常多,走的是RTPS协议,

图片[9]-2022红明谷ctf MISC writeUP-魔法少女雪殇

是符合这里面的逻辑的

继续

图片[10]-2022红明谷ctf MISC writeUP-魔法少女雪殇

重点在这,如果mode是onboard,那么他就会读取flag,

图片[11]-2022红明谷ctf MISC writeUP-魔法少女雪殇

然后读取后半部分,转换为hex

图片[12]-2022红明谷ctf MISC writeUP-魔法少女雪殇

然后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)))
图片[13]-2022红明谷ctf MISC writeUP-魔法少女雪殇

获得后半段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这几个参数的前面,他们是一组流量包

图片[14]-2022红明谷ctf MISC writeUP-魔法少女雪殇

最后解密即可

图片[15]-2022红明谷ctf MISC writeUP-魔法少女雪殇

获得整体flag

© 版权声明
THE END
喜欢就支持一下吧
点赞2 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情