Natas20 Writeup(Session登录,注入参数)
Natas20:

读取源码,发现把sessionID存到了文件中,按键值对存在,以空格分隔,如果$_SESSION["admin"]==1,则成功登陆,得到flag。并且通过查询所提交的参数,也会被存到文件中,因此,可以采取注入键值对admin 1的方式来实现修改。
使用burp抓包,将name参数修改为:name=xxx%0Aadmin 1,得到flag。
源码解析:
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas20", "pass": "<censored>" };</script></head>
<body>
<h1>natas20</h1>
<div id="content">
<? //debug($msg)表示打开了调试信息,可以通过在URL的末尾添加 /index.php?debug来查看调试消息 $msg
function debug($msg) { /* {{{ */
//php中预定义的 $_GET 变量用于收集来自 method="get" 的表单中的值。
//array_key_exists(key,array)函数检查键名是否存在于数组中,如果键名存在则返回 TRUE,如果键名不存在则返回 FALSE。
if(array_key_exists("debug", $_GET)) {
print "DEBUG: $msg<br>";
}
}
/* }}} */
function print_credentials() { /* {{{ */
//主要功能就是判断session[admin]=1后显示密码
if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
print "You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas21\n";
print "Password: <censored></pre>";
} else {
print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.";
}
}
/* }}} */ /* we don't need this */
function myopen($path, $name) {
//debug("MYOPEN $path $name");
return true;
} /* we don't need this */
function myclose() {
//debug("MYCLOSE");
return true;
} function myread($sid) {
debug("MYREAD $sid");
//strspn(string,charlist):返回在字符串string中包含charlist中字符的数目
//判断sid是否是由数字/字母/-组成,如果不是,则无效
if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
debug("Invalid SID");
return "";
}
//session_save_path() - 返回当前会话的保存路径。
$filename = session_save_path() . "/" . "mysess_" . $sid;
if(!file_exists($filename)) {
debug("Session file doesn't exist");
return "";
}
debug("Reading from ". $filename);
$data = file_get_contents($filename);
$_SESSION = array();
//使用换行符分割data数据
foreach(explode("\n", $data) as $line) {
debug("Read [$line]");
//使用空格符分割line数据,返回的数组包含2个元素,最后那个元素包含line的剩余部分
$parts = explode(" ", $line, 2);
if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
}
//session_encode — 将当前会话数据编码为一个字符串
return session_encode();
} function mywrite($sid, $data) {
// $data contains the serialized version of $_SESSION
// but our encoding is better
debug("MYWRITE $sid $data");
// make sure the sid is alnum only!!
//判断sid是否是由数字/字母/-组成,如果不是,则无效
if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
debug("Invalid SID");
return;
}
$filename = session_save_path() . "/" . "mysess_" . $sid;
$data = "";
debug("Saving in ". $filename);
//ksort — 对数组按照键名排序
ksort($_SESSION);
//foreach 遍历给定的数组。每次循环中,当前单元的键名被赋给$key、当前单元的值被赋给$value,并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。
foreach($_SESSION as $key => $value) {
debug("$key => $value");
//给data赋值
$data .= "$key $value\n";
}
//将data存在文件中
file_put_contents($filename, $data);
//改变文件模式:Read and write for owner, nothing for everybody else
chmod($filename, 0600);
} /* we don't need this */
function mydestroy($sid) {
//debug("MYDESTROY $sid");
return true;
}
/* we don't need this */
function mygarbage($t) {
//debug("MYGARBAGE $t");
return true;
} session_set_save_handler(
"myopen",
"myclose",
"myread",
"mywrite",
"mydestroy",
"mygarbage");
session_start(); if(array_key_exists("name", $_REQUEST)) {
$_SESSION["name"] = $_REQUEST["name"];
debug("Name set to " . $_REQUEST["name"]);
} print_credentials(); $name = "";
if(array_key_exists("name", $_SESSION)) {
$name = $_SESSION["name"];
} ?> <form action="index.php" method="POST">
Your name: <input name="name" value="<?=$name?>"><br>
<input type="submit" value="Change name" />
</form>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
natas20-sourcecode.html
我们来看看每个函数的作用:
debug($msg)函数表示打开了调试信息,可以通过在URL的末尾添加 /index.php?debug来查看调试消息 $msg。
访问之后将看到一些提示信息:
DEBUG: MYWRITE 9l3s4uq5gqk1u5c3mk8gti5sr6 name|s::"admin";
DEBUG: Saving in /var/lib/php5/sessions//mysess_9l3s4uq5gqk1u5c3mk8gti5sr6
DEBUG: name => admin DEBUG: MYREAD 9l3s4uq5gqk1u5c3mk8gti5sr6
DEBUG: Reading from /var/lib/php5/sessions//mysess_9l3s4uq5gqk1u5c3mk8gti5sr6
DEBUG: Read [name admin]
DEBUG: Read []

mywrite和myread是两个关键函数,它们的作用是管理会话状态。
function myread($sid) {
debug("MYREAD $sid");
//strspn(string,charlist):返回在字符串string中包含charlist中字符的数目
//判断sid是否是由数字/字母/-组成,如果不是,则无效
if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
debug("Invalid SID");
return "";
}
//session_save_path() - 返回当前会话的保存路径。
$filename = session_save_path() . "/" . "mysess_" . $sid;
if(!file_exists($filename)) {
debug("Session file doesn't exist");
return "";
}
debug("Reading from ". $filename);
$data = file_get_contents($filename);
$_SESSION = array();
//使用换行符分割data数据
foreach(explode("\n", $data) as $line) {
debug("Read [$line]");
//使用空格符分割line数据,返回的数组包含2个元素,最后那个元素包含line的剩余部分
$parts = explode(" ", $line, 2);
if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
}
//session_encode — 将当前会话数据编码为一个字符串
return session_encode();
}
function mywrite($sid, $data) {
// $data contains the serialized version of $_SESSION
// but our encoding is better
debug("MYWRITE $sid $data");
// make sure the sid is alnum only!!
//判断sid是否是由数字/字母/-组成,如果不是,则无效
if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
debug("Invalid SID");
return;
}
$filename = session_save_path() . "/" . "mysess_" . $sid;
$data = "";
debug("Saving in ". $filename);
//ksort — 对数组按照键名排序
ksort($_SESSION);
//foreach 遍历给定的数组。每次循环中,当前单元的键名被赋给$key、当前单元的值被赋给$value,并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。
foreach($_SESSION as $key => $value) {
debug("$key => $value");
//给data赋值
$data .= "$key $value\n";
}
//将data存在文件中
file_put_contents($filename, $data);
//改变文件模式:Read and write for owner, nothing for everybody else
chmod($filename, 0600);
}
简单来说,myread首先对sid(第一次由服务器自动生成并保存在cookie中)进行校验,若非字母/数字则不返回会话状态。
若sid合法,则进入相关目录寻找/读取文件,若是老的会话/文件已经删除会新建文件保存会话,文件读取完后将session的最后一对键值覆盖到第一的位置。
mywrite则会在会话结束的时候重新读取session,并对session进行一次ksort,将排序后的键值对重新写入文件。
print_credentials()函数的主要功能,是判断$_SESSION["admin"] == 1后显示密码。
由于源码里面没有向$SESSION里面添加admin的键值对,默认情况下,$_SESSION中唯一的key是name,其值通过/index.php中的表单提交进行设置。
我们可以通过对name键值对进行注入:将data里面的值变为:name xxx\nadmin 1\n。所以应该输入xxx\nadmin 1,将其进行URL编码后进行提交。
换行符对应的URL编码为%0A,所以最终应该输入xxx%0Aadmin 1提交。
当然不能在网页中直接输入xxx%0Aadmin 1提交,因为会被编码成xxx%250Aadmin+1,失去了我们的本意。
正确的做法是,使用burp抓包,修改name参数值为xxx%0Aadmin 1,第一次会显示regular,因为没有文件/状态可以读取,session里还是没有Admin的,会话关闭后xxx\nadmin 1就会被写入到状态中,下次登录后session就会加入admin 1了。

flag:IFekPyrQXftziDEsUr3x21sYuahypdgJ
参考:
https://www.cnblogs.com/ichunqiu/p/9554885.html
https://www.cnblogs.com/liqiuhao/p/6882068.html
https://www.freebuf.com/column/182518.html
Natas20 Writeup(Session登录,注入参数)的更多相关文章
- Natas18 Writeup(Session登录,暴力破解)
Natas18: 一个登录界面,查看源码,发现没有连接数据库,使用Session登录,且$maxid设定了不大的上限,选择采取爆破. 源码解析: <html> <head> & ...
- 第二百六十九节,Tornado框架-Session登录判断
Tornado框架-Session登录判断 Session需要结合cookie来实现 Session的理解 1.用户登录系统时,服务器端获取系统当前时间,进行nd5加密,得到加密后的密串 2.将密串作 ...
- SpringMVC - 个人对@ModelAttribute的见解 和 一些注入参数、返回数据的见解
2016-8-23修正. 因为对modelattribute这个注解不了解,所以在网上搜寻一些答案,感觉还是似懂非懂的,所以便自己测试,同时还结合网上别人的答案:最后得出我自己的见解和结果,不知道正确 ...
- oracle中的sys用户(修改密码)/////Oracle删除表空间的同时删除数据文件 ///// Oracle中如何保证用户只有一个session登录
oracle中的sys用户(修改密码) (2011-07-01 09:18:11) 转载▼ 标签: it 分类: oracle 概念: SYS用户是Oracle中权限最高的用户,而SYSTEM是一个用 ...
- spring注入参数详解
spring注入参数详解 在Spring配置文件中, 用户不但可以将String, int等字面值注入到Bean中, 还可以将集合, Map等类型的数据注入到Bean中, 此外还可以注入配置文件中定义 ...
- spring mvc 3.1的自动注入参数遇到的问题
在网上下载了xheditor作为页面的编辑器,编辑内容后post到后台保存,后台方法用spring mvc的自动注入的方式接收参数. 这种方式在各个浏览器下运行良好,但是在ie11下发现,从word. ...
- Spring属性注入、构造方法注入、工厂注入以及注入参数(转)
Spring 是一个开源框架. Spring 为简化企业级应用开发而生(对比EJB2.0来说). 使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能.Spring ...
- Spring 依赖注入(二、注入参数)
注入参数基本分7类: 1.基本类型值 2.注入bean 3.内部bean 4.注入null值 5.级联属性 6.List,Set,Map集合的注入 7.properties文件的注入(和集合注入基本是 ...
- HandlerMethodArgumentResolver完美解决 springmvc注入参数多传报错
作为一个后端开发,能友好兼容前端参数传入错误等问题,在前端发布不小心多传一个参数导致系统错误的问题,一个广告系统是零容忍的,所以为了不犯错误,后端接收参数必须摒弃spring 的自动注入@Reques ...
- mybatis中collection子查询注入参数为null
具体实现参照网上,但是可能遇到注入参数为null的情况,经过查阅及自己测试记录一下: 子查询的参数中,有<if test="">之类,需要指定别名,通过 http:// ...
随机推荐
- 最简化的DirectX 11开发环境的配置 VS2010
转载自:http://blog.csdn.net/zhmxy555/article/details/7672101 在编写基于DirectX 11的应用程序之前,我们当然需要在IDE中加入Direct ...
- HttpClient-get请求/Post请求/Post-Json/Header
1.Pom文件添加httpClient 依赖 <dependency> <groupId>org.apache.httpcomponents</groupId> & ...
- 为啥Waymo和Uber间的官司,这么难界定?
自动驾驶作为被众多巨头和创业者看好的技术及领域,已有众多企业涉足其中.在自动驾驶尚未完全落地.制定统一标准前,每家企业都在争分夺秒地进行研发,试图率先抢占自动驾驶的高地.毕竟一旦成功,就能够创造巨大的 ...
- resin远程调试debug
wangqiaowqo Resin 远程debug Resin Windows下提升Resin默认的虚拟机内存大小 httpd.exe -Xmx1024m 参考外部文章 Resin远程debug配置文 ...
- 使用Google App Engine开始新的网站开发学习
继长时间的迷茫后,我发现还是回归php网站开发更适合我,或者没有那么深刻,但至少要做点事情.不知道以后将从事什么样的工作,但现在找点事情做还是很好的.所以,为了激发我学习的热情,我在网上搜了一下免费云 ...
- 用shell脚本新建文件并自动生成头说明信息
目标: 新建文件后,直接给文件写入下图信息 代码实现: [root@localhost test]# vi AutoHead.sh #!/bin/bash #此程序的功能是新建shell文件并自动生成 ...
- 杂记:Linux下gcc升级
公司要求,需要在CentOS6.5系统下进行一些测试.因为编写的测试程序中使用了一些C++11之后新增的特性,而CentOS6.5中安装的gcc版本为4.4.7,并不支持C++11,所以需要对gcc进 ...
- golang xml解析
第二章里还提到了xml的解析部分.之前有想整理下encoding包下常用的几个文件格式的处理.这次刚好整理下xml的部分.先上例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 ...
- MHRD_Guide
@(Steam蒸汽动力)[MHRD|Game|How To] [CH]游戏解决方案 1.解决方案将作为完整的代码呈现,部分结决方案将有理论分析或图解. 2.根据记录板判断,解决方案远非理想. 3.慎重 ...
- Spring Boot 2.x基础教程:使用MyBatis访问MySQL
之前我们已经介绍了两种在Spring Boot中访问关系型数据库的方式: 使用spring-boot-starter-jdbc 使用spring-boot-starter-data-jpa 虽然Spr ...