清除wnTKYg木马 –高占用cpu

今天偶然发现服务器cpu占用率一直是90%,top查看了发现是一个名为wnTKYg的进程。

网上查找说是一个挖矿木马,清理之后做个记录。

尝试pkill -9 wnTKYg杀死进程,发现没过多久又出现了

中毒原因应该是redis没有设密码或者是弱口令,先关了redis防止再次中招

systemctl stop redis

接下来

  • 杀死占用内存大的程序

杀进程:

pkill -9 wnTKYg

pkill -9 ddg.1009

  • 然后根据查看到的程序路径,进入目录内,将目录里的文件删除

如果/root/.ssh/下有异常文件或记录:rm -rf /root/.ssh/*

查找wnTKYg进程目录:find / -name wnTKYg*

删除wnTKYg进程文件:rm -rf /tmp/wnTKYg

查找wnTKYg守护进程目录:ps -aux|grep ddg

删除wnTKYg守护进程文件,文件后缀可能不同:rm -rf /tmp/ddg.1009

删除可疑文件:

rm -rf /tmp/Aegis-\<Guid5A2C30A2−A87D−490A−9281−6765EDAD7CBA\>

rm -rf /usr/local/aegis/Aegis-\<Guid5A2C30A2−A87D−490A−9281−6765EDAD7CBA\>

删除wnTKYg定时任务:rm -rf /var/spool/cron

检查是否存在残留文件并清理…

 

  • 之后检查开机自动启动或者定时任务里是否有不正常自启动和定时任务
    我中的病毒是写在定时任务的一个任务。指向一个网站自动下载一个脚本。

ps x 或 top 查看是否还有木马进程

到此应该已经清理完毕

问题总结
这样的病毒是直接远程连接redis,一般redis都是root安装的,连接redis也就掌握了root权限,它可以往你的定时任务里写内容。
中这样的病毒大多都是因为redis没有设置密码,存在着很大的安全漏洞,所以大家要设置redis密码,并且更改redis的端口。安装时最好别用root安装。


安全性攻击

1、SQL注入
SQL注入是一种恶意攻击,用户利用在表单字段输入SQL语句的方式来影响正常的SQL执行。

$username = $_POST['username'];
$query = "select * from auth where username = '".$username."'";
echo $query;

上面的代码,在第一行没有过滤或转义用户输入的值($_POST[‘username’])。因此查询可能会失败,甚至会损坏数据库,这要看$username是否包含变换你的SQL语句到别的东西上。

防止SQL注入

  • 使用mysql_real_escape_string()过滤数据
  • 手动检查每一数据是否为正确的数据类型
  • 使用预处理语句并绑定变量

使用准备好的预处理语句

  • 分离数据和SQL逻辑
  • 预处理语句将自动过滤(如:转义)
  • 把它作为一个编码规范,可以帮助团队里的新人避免遇到以上问题
$query = 'select name, district from city where countrycode=?';
if ($stmt = $db->prepare($query) )
{
    $countrycode = 'hk';
    $stmt->bind_param("s", $countrycode);  
    $stmt->execute();
    $stmt->bind_result($name, $district);
    while ( $stmt ($stmt->fetch() ){
        echo $name.', '.$district;
        echo '<br />';
    }
    $stmt->close();
}

2、XSS攻击

XSS(跨站点脚本攻击)是一种攻击,由用户输入一些数据到你的网站,其中包括客户端脚本(通常JavaScript)。如果你没有过滤就输出数据到另一个web页面,这个脚本将被执行。

接收用户提交的文本内容

<?php
if (file_exists('comments')) {
    $comments = get_saved_contents_from_file('comments');
} else {
   $comments = '';
}

if (isset($_POST['comment'])) {
   $comments .= '<br />' . $_POST['comment'];
   save_contents_to_file('comments', $comments);
}
>

输出内容给(另一个)用户

<form action='xss.php' method='POST'>
    Enter your comments here: <br />
    <textarea name='comment'></textarea> <br />
    <input type='submit' value='Post comment' />
</form><hr /><br />

<?php echo $comments; ?>

将会发生什么事?

  • 烦人的弹窗
  • 刷新或重定向
  • 损坏网页或表单
  • 窃取cookie
  • AJAX(XMLHttpRequest)

防止XSS攻击

为了防止XSS攻击,使用PHP的htmlentities()函数过滤再输出到浏览器。htmlentities()的基本用法很简单,但也有许多高级的控制,请参阅 XSS速查表

3、会话固定

会话安全,假设一个PHPSESSID很难猜测。然而,PHP可以接受一个会话ID通过一个Cookie或者URL。因此,欺骗一个受害者可以使用一个特定的(或其他的)会话ID 或者钓鱼攻击。

4、会议捕获和劫持

这是与会话固定有着同样的想法,然而,它涉及窃取会话ID。如果会话ID存储在Cookie中,攻击者可以通过XSS和JavaScript窃取。如果会话ID包含在URL上,也可以通过嗅探或者从代理服务器那获得。

防止会话捕获和劫持

  • 更新ID
  • 如果使用会话,请确保用户使用SSL

5、跨站点请求伪造(CSRF)

CSRF攻击,是指一个页面发出的请求,看起来就像是网站的信任用户,但不是故意的。它有许多的变体,比如下面的例子:

<?php
session_start();
include("../DB.class.php");
$db = new DB();
if(empty($_SESSION["uid"]))
{
    echo "未登录";    
}
else
{    $qian = $_GET["qian"];    
    $sql = "update login set account = account-{$qian} where username = '{$_SESSION['uid']}'";
    echo $sql;
    $db->Query($sql,1);    
}

这种get传值方式会在地址栏显示提交数据,在不关闭页面的情况下,再做一个页面,把地址复制进去,这样请求这个页面,也会将数据库中数据改掉。

而如果改成POST方式,可以减少这种情况,也可以在表单中用隐藏域多提交一条数据。

<?php
session_start();
include("../DB.class.php");
$db = new DB();

if(empty($_SESSION["uid"]))
{
    echo "未登录";    
}
else
{
    $uid =md5($_SESSION["uid"]);
    $str = $_POST["xinxi"];    
    if($uid == $str)
    {
        $qian = $_POST["qian"];    
        $sql = "update login set account = account-{$qian} where username = '{$_SESSION['uid']}'";
        echo $sql;
        $db->Query($sql,1);
    }
}

但是这种方式也不能完全避免CSRF攻击,即使用MD5加密,也还是有人可以解出来,最好的方法还是使用验证码。你不知道验证码是怎么生成的,就无法进行CSRF攻击。

防止跨站点请求伪造

一般来说,确保用户来自你的表单,并且匹配每一个你发送出去的表单。有两点一定要记住:

  • 对用户会话采用适当的安全措施,例如:给每一个会话更新id和用户使用SSL。
  • 生成另一个一次性的令牌并将其嵌入表单,保存在会话中(一个会话变量),在提交时检查它。

6、代码注入

代码注入是利用计算机漏洞通过处理无效数据造成的。问题出在,当你不小心执行任意代码,通常通过文件包含。写得很糟糕的代码可以允许一个远程文件包含并执行。如许多PHP函数,如require可以包含URL或文件名,例如:

<form>Choose theme:
    <select name = theme>
        <option value = blue>Blue</option>
        <option value = green>Green</option>
        <option value = red>Red</option>
    </select>
    <input type = submit>
</form>
<?php
    if($theme) {
        require($theme.'.txt');
    }
?>

在上面的例子中,通过传递用户输入的一个文件名或文件名的一部分,来包含以”http://”开头的文件。

防止代码注入

  • 过滤用户输入
  • 在php.ini中设置禁用allow_url_fopen和allow_url_include。这将禁用require/include/fopen的远程文件。

其他的一般原则

a. 不要依赖服务器配置来保护你的应用,特别是当你的web服务器/PHP是由你的ISP管理,或者当你的网站可能迁移/部署到别处,未来再从别处迁移/部署在到其他地方。请在网站代码中嵌入带有安全意识的检查/逻辑(HTML、JavaScript、PHP等等)。
b. 设计服务器端的安全脚本:
—例如,使用单行执行 – 单点身份验证和数据清理
—例如,在所有的安全敏感页面嵌入一个PHP函数/文件,用来处理所有登录/安全性逻辑检查
c. 确保你的代码更新,并打上最新补丁。

总结

SQL注入只需过滤提交的字符串即可,XSS攻击用PDO预处理,CSRF攻击用验证码就可解决。

有些黑客会伪造FILE数组上传,如何辨别:move_upload_file()可以判断是不是真实文件。

以后做项目,有关安全性的地方一定要谨慎,千万不要轻易相信用户上传或提交的任何数据,一定要进行正确处理


流量劫持:DNS劫持、CDN劫持、网关劫持!

1、DNS劫持

DNS劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能反应或访问的是假网址。

如何应对DNS劫持?

DNS劫持(DNS钓鱼攻击)十分凶猛且不容易被用户感知,黑客利用宽带路由器的缺陷对用户DNS进行篡改——用户只要浏览一下黑客所掌控的WEB页面,其宽带路由器的DNS就会被黑客篡改,因为该WEB页面设有特别的恶意代码,所以可以成功躲过安全软件检测,导致大量用户被DNS钓鱼诈骗。

为此遇到这种情况我们需要手动修改DNS、修改路由器密码。

2、CDN劫持

CDN能加速大家都知道,但其中原理不少人都不清楚。其实,CDN本身就是一种DNS劫持,只不过是良性的。 不同于黑客强制DNS把域名解析到自己的钓鱼IP上,CDN则是让DNS主动配合,把域名解析到临近的服务器上。

这台服务器同样也开启了HTTP代理,让用户感觉不到CDN的存在。

不过CDN不像黑客那样贪心,劫持用户所有流量,它只『劫持』用户的静态资源访问,对于之前用户访问过的资源,CDN将直接从本地缓存里反馈给用户,因此速度有了很大的提升。

然而,只要是有缓存的地方,都是大有可为的。

一旦CDN服务器遭受入侵,硬盘上的缓存文件就岌岌可危了,网页被注入脚本,可执行文件被感染,一大波僵尸即将出现。

如何应对CDN劫持?

防范措施:感觉运营商不靠谱的话,换个第三方不带加速的 DNS,或许就不会解析到 CDN 服务器上了。

不少 CDN 黑白通吃,为了省流量不按套路出牌,超过了缓存时间也不更新,甚至还有忽略 URL 问号后面的,导致程序猿们在资源更新的问题上头疼不已。

3、网关劫持

交换机的出现逐渐淘汰了集线器。交换机会绑定MAC地址和接口,数据包最终只发往一个终端。因此只要事先配置好MAC对应的接口,理论上非常安全了。

不过,很少有人会那么做,大多为了偷懒,直接使用了设备默认的模式——自动学习。设备根据某个接口发出的包,自动关联该包的源地址到此接口。

然而这种学习并不智能,甚至太过死板,任何一个道听途说都会当作真理。用户发送一个自定义源MAC地址的包是非常容易的,因此交换机成了非常容易被忽悠的对象。只要伪造一个源地址,就能将这个地址关联到自己的接口上,以此获得受害者的流量。

不过,受害者接着再发出一个包,绑定关系又恢复原先正常的。因此只要比谁发的频繁,谁就能竞争到这个MAC地址的接收权。如果伪造的是网关地址,交换机就误以为网关电缆插到你接口上,网络环境里的出站流量瞬间都到了你这里。

当然,除非你有其他出站渠道,可以将窃取的数据代理出去;否则就别想再转发给被你打垮的真网关了,被劫持的用户也就没法上外网。所以这招危害性不是很大,但破坏性很强,可以瞬间集体断网。

防范措施:机器固定的网络尽量绑定MAC和接口吧。貌似大多数网吧都绑定了MAC和接口,极大增强了链路层的安全性。同时,独立的子网段尽可能划分VLAN,避免过大的广播环境


php性能优化

请记住你的php脚本性能,很多时候依赖于你的php版本、你的web server环境和你的代码的复杂度

  • 使用输出缓冲区

当你的脚本尝试着渲染的时候,php会使用内存缓存区保存所有的数据。缓存区可能让你的页面看起来很慢,原因是缓冲区填满所有要响应的数据之后再把结果响应给用户。幸运的是,你能够做一下改变,迫使php强行在缓冲区填满之前把数据响应给用户,这样就会让你的网站看起来更快一些。

  • 避免写setters和getters
当你写php类的时候,你可以直接操作对象属性,这样能帮助你节省时间和提升你的脚本性能。而不是setters和getters。
下面是一些案例:dog类通过使用setName()和getName()方式来操作name属性
class dog {
    public $name = '';
 
 public function setName($name) {
    $this->name = $name;
 }
 
 public function getName() {
    return $this->name;
 }
}
注意:setName()和getName()除了存储和返回name属性外,没做任何工作。
$rover = new dog();
$rover->setName('rover');
echo $rover->getName();

直接设置和访问name属性,性能能提升100%,而且也能缩减开发时间!

$rover = new dog();
$rover->name = 'rover';
echo $rover->name;
  • 没有原因不要copy变量

不要为了使代码更加”干净”,常常把已经定义的变量重新赋值给另一个变量。这实际上就导致了双重内存的消耗(当改变变量的时候),这就导致脚本的性能下降。比如一个用户把一个512KB的变量在额外插入给另一个变量,那么就会导致1MB的内存被消耗掉。

$description = strip_tags($_POST['description']);
echo $description;

上面的代码没有任何原因,复制了一遍变量。你仅需要使用内联的方式简单输出变量,而不用额外的消耗内存。

echo strip_tags($_POST['description']);

由微信开发获取推送数据,比较$POST 、$HTTP_RAW_POST_DATA、php://input三者之间的区别

  • $POST

$_POST是我们最常用的获取POST数据的方式,它是以关联数组方式组织提交的数据,并对此进行编码处理,如urldecode,甚至编码转换,识别的数据类型是PHP默认识别的数据类型 application/x-www.form-urlencoded

无法解析如text/xml,application/json等非 application/x-www.form-urlencoded 数据类型的内容

  • HTTP_RAW_POST_DATA

前面说过PHP默认识别的数据类型是application/x-www.form-urlencoded,用Content-Type=application/json 类型,提交的POST数据这时候 $_POST 就无法获取到了,但是使用 $GLOBALS[‘HTTP_RAW_POST_DATA’] 可以获取到。因为在PHP无法识别Content-Type的时候,就会把 POST 数据填入到 $HTTP_RAW_POST_DATA 中。

需要设置 php.ini 中的 always_populate_raw_post_data 值为 On 才会生效
当$_POST 与 php://input可以取到值时 $HTTP_RAW_POST_DATA 为空
不能用于 enctype=”multipart/form-data”
PHP7中已经移除了这个全局变量,用 php://input 替代

  • php://input

php://input 可通过输入流以文件读取方式取得未经处理的POST原始数据,允许读取 POST 的原始数据。和 $HTTP_RAW_POST_DATA 比起来,它给内存带来的压力较小

不需要任何特殊的 php.ini 设置
不能用于 enctype=”multipart/form-data”


PHP QR Code生成二维码

4PHP QR Code是一个PHP二维码生成类库,利用它可以轻松生成二维码。

下载官网提供的类库后,只需要使用phpqrcode.php就可以生成二维码了,当然您的PHP环境必须开启支持GD2

phpqrcode.php提供了一个关键的png()方法,其中
参数$text表示生成二位的的信息文本;
参数$outfile表示是否输出二维码图片 文件,默认否;
参数$level表示容错率,也就是有被覆盖的区域还能识别,分别是 L(QR_ECLEVEL_L,7%),M(QR_ECLEVEL_M,15%),Q(QR_ECLEVEL_Q,25%),H(QR_ECLEVEL_H,30%);
参数$size表示生成图片大小,默认是3;参数$margin表示二维码周围边框空白区域间距值;
参数$saveandprint表示是否保存二维码并显示。

  • 调用PHP QR Code非常简单,如下代码即可生成一张内容为”http://www.yilingdian.cn”的二维码.
include 'phpqrcode.php'; 
QRcode::png('http://www.yilingdian.cn');
  • 那么实际应用中,我们会在二维码的中间加上自己的LOGO,已增强宣传效果。那如何生成含有logo的二维码呢?其实原理很简单,先使用PHP QR Code生成一张二维码图片,然后再利用php的image相关函数,将事先准备好的logo图片加入到刚生成的原始二维码图片中间,然后重新生成一张新 的二维码图片。
include_once 'phpqrcode.php';

// 生成的文件名
$file = './z.png';

$value = 'http://www.yilingdian.cn'; //二维码内容

$errorCorrectionLevel = 'M';
//容错级别  纠错级别:L、M、Q、H //L时带logo会无法识别,M、Q、H能识别图片
$matrixPointSize = 5;//生成图片大小  点的大小:1到10
//生成二维码图片
QRcode::png($value,$file , $errorCorrectionLevel, $matrixPointSize, 2,true);//$file=false时浏览器直接输出图片

//$logo为网络地址会报错,添图片绝对路径,不能生成logo二维码
$logo = 'D:\20170706110111.png';//准备好的logo图片
$QR = $file;//已经生成的原始二维码图

if ($logo !== FALSE) {
    $QR = imagecreatefromstring(file_get_contents($QR));
    $logo = imagecreatefromstring(file_get_contents($logo));
    $QR_width = imagesx($QR);//二维码图片宽度
    $QR_height = imagesy($QR);//二维码图片高度
    $logo_width = imagesx($logo);//logo图片宽度
    $logo_height = imagesy($logo);//logo图片高度
    $logo_qr_width = $QR_width / 5;
    $scale = $logo_width/$logo_qr_width;
    $logo_qr_height = $logo_height/$scale;
    $from_width = ($QR_width - $logo_qr_width) / 2;
    //重新组合图片并调整大小
    imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width,
        $logo_qr_height, $logo_width, $logo_height);
}
//输出图片
imagepng($QR, '_z.png');
echo '<img src="_z.png">';

由于二维码允许有一定的容错性,一般的二维码即使在遮住部分但仍然能够解码,经常我们扫描二维码的时候扫描到甚至不到一半时就能解码扫描结果,这是因为生成器会将部分信息重复表示来提高其容错度,这就是为什么我们在二维码中间加个LOGO图片并不影响解码结果的原因。


微信上传永久素材

  • 直接调用addMaterial方法上传永久素材
  • 上传时素材路径为绝对路径
  • curl post上传注意PHP版本
/**
 * @type 素材类型
 * @fileName 文件名
 * @title $type=video,视频标题
 * @introduction $type=video,视频描述
 * @return json
 */
public function addMaterial ($type,$fileName,$title='',$introduction='')
{
    if ($type=='uploadimg') {
        $urlType='uploadimg';
    }else{
        $urlType='addMaterial';
    }
    $url=$this->interfaceUrl($urlType);//接口url
    $realPath=$this->filePath($type,$fileName);
    $data['media']=$realPath;
    if ($type=='video') {
        $data['description']='{
                                 "title":"'.$title.'",
                          "introduction":"'.$introduction.'"
                            }';
    }
    $res=util::_curl($url,false,$data);
    return $res;
}

 

/**
 * @mediaType 素材类型
 * @param $fileName 1.jpg
 * @return string
 */
public function filePath ($mediaType,$fileName)
{
    $mediaPath=$this->mediaPath($mediaType);
    $realPath=$mediaPath.$fileName;
    if (class_exists('CURLFile')) {
        $filePath=new CURLFile($realPath);
    }else{
        $filePath='@'.$realPath;//php < 5.6
    }
    return $filePath;
}

 

/**
 * @mediaType 上传素材类型
 * @return string
 */
public function mediaPath($mediaType)
{
    switch ($mediaType) {
        case 'image':
            $mediaPath=IMAGE_PATH;
            break;
        case 'voice':
            $mediaPath=VOICE_PATH;
            break;
        case 'video':
            $mediaPath=VIDEO_PATH;
            break;
        case 'thumb':
            $mediaPath=THUMB_PATH;
            break;
        case 'uploadimg':
            $mediaPath=UPLOAD_IMG_PATH;
            break;
        default:
            $mediaPath='';
            break;
    }
    return $mediaPath;
}


MYSQL强制使用索引和禁止使用索引

1、mysql强制使用索引:force index(索引名或者主键PRI)

例如:

select * from table force index(PRI) limit 2;(强制使用主键)

select * from table force index(ziduan1_index) limit 2;(强制使用索引”ziduan1_index”)

select * from table force index(PRI,ziduan1_index) limit 2;(强制使用索引”PRI和ziduan1_index”)

2、mysql禁止某个索引:ignore index(索引名或者主键PRI)

例如:

select * from table ignore index(PRI) limit 2;(禁止使用主键)

select * from table ignore index(ziduan1_index) limit 2;(禁止使用索引”ziduan1_index”)

select * from table ignore index(PRI,ziduan1_index) limit 2;(禁止使用索引”PRI,ziduan1_index”)


利用curl post方式上传文件

PHP的cURL支持通过给CURL_POSTFIELDS传递关联数组(而不是字符串)来生成multipart/form-data的POST请求

if (class_exists('CURLFile')) {
    $realPath=new CURLFile(realpath($filePath));
}else{
    $realPath='@'.realpath($filePath);//php < 5.6
}
$data['media']=$realPath;
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

不建议下面这种写法判断php版本

if (version_compare(phpversion(), '5.4.0') >= 0)