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登录,注入参数)的更多相关文章

  1. Natas18 Writeup(Session登录,暴力破解)

    Natas18: 一个登录界面,查看源码,发现没有连接数据库,使用Session登录,且$maxid设定了不大的上限,选择采取爆破. 源码解析: <html> <head> & ...

  2. 第二百六十九节,Tornado框架-Session登录判断

    Tornado框架-Session登录判断 Session需要结合cookie来实现 Session的理解 1.用户登录系统时,服务器端获取系统当前时间,进行nd5加密,得到加密后的密串 2.将密串作 ...

  3. SpringMVC - 个人对@ModelAttribute的见解 和 一些注入参数、返回数据的见解

    2016-8-23修正. 因为对modelattribute这个注解不了解,所以在网上搜寻一些答案,感觉还是似懂非懂的,所以便自己测试,同时还结合网上别人的答案:最后得出我自己的见解和结果,不知道正确 ...

  4. oracle中的sys用户(修改密码)/////Oracle删除表空间的同时删除数据文件 ///// Oracle中如何保证用户只有一个session登录

    oracle中的sys用户(修改密码) (2011-07-01 09:18:11) 转载▼ 标签: it 分类: oracle 概念: SYS用户是Oracle中权限最高的用户,而SYSTEM是一个用 ...

  5. spring注入参数详解

    spring注入参数详解 在Spring配置文件中, 用户不但可以将String, int等字面值注入到Bean中, 还可以将集合, Map等类型的数据注入到Bean中, 此外还可以注入配置文件中定义 ...

  6. spring mvc 3.1的自动注入参数遇到的问题

    在网上下载了xheditor作为页面的编辑器,编辑内容后post到后台保存,后台方法用spring mvc的自动注入的方式接收参数. 这种方式在各个浏览器下运行良好,但是在ie11下发现,从word. ...

  7. Spring属性注入、构造方法注入、工厂注入以及注入参数(转)

    Spring 是一个开源框架. Spring 为简化企业级应用开发而生(对比EJB2.0来说). 使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能.Spring ...

  8. Spring 依赖注入(二、注入参数)

    注入参数基本分7类: 1.基本类型值 2.注入bean 3.内部bean 4.注入null值 5.级联属性 6.List,Set,Map集合的注入 7.properties文件的注入(和集合注入基本是 ...

  9. HandlerMethodArgumentResolver完美解决 springmvc注入参数多传报错

    作为一个后端开发,能友好兼容前端参数传入错误等问题,在前端发布不小心多传一个参数导致系统错误的问题,一个广告系统是零容忍的,所以为了不犯错误,后端接收参数必须摒弃spring 的自动注入@Reques ...

  10. mybatis中collection子查询注入参数为null

    具体实现参照网上,但是可能遇到注入参数为null的情况,经过查阅及自己测试记录一下: 子查询的参数中,有<if test="">之类,需要指定别名,通过 http:// ...

随机推荐

  1. <JZOJ5937&luogu3944>斩杀计划&肮脏的牧师

    第一次写桶排相关题.... #include<cstdio> #include<iostream> #define rint register int template < ...

  2. Java多态详解

    package QianFeng02; //多态 public class Polymorphic { public static void main(String[] args){ // HomeC ...

  3. NEWMING

    这里只是列举一些常用的文件操作命令. cd 跳转切换目录 # 格式:cd dirname 比如在打开用户主目录盘下的 myidoc 文件夹 cd ~/myidoc 跳转到当前目录的上一级 cd ../ ...

  4. PAT B1080 MOOC期终成绩(C++)

    PAT甲级目录 | PAT乙级目录 题目描述 B1080 MOOC期终成绩 解题思路 可利用 map 将字符串型的学号转换为整型的序号,方便查找.输入全部成绩后,遍历每个学生同时计算最终成绩,然后将成 ...

  5. MAC使用nginx分发80至8080端口

    由于项目必须要启动80端口,但是mac系统中非root用户无法直接使用1024以下的端口 2.释放apache的80端口 由于Mac OS是自带Apache服务的,它本身占用了80端口,首先你需要将A ...

  6. 从iPhone下滑 看科技转型之困

    看科技转型之困" title="从iPhone下滑 看科技转型之困"> 毫无疑问,苹果正在面临一次关键转型.最近苹果股价较高点134.54美元下跌21%以上的实事, ...

  7. 从高知社区知乎变故事会,看论坛IP的夹缝生存

    ​​ ​ ​ "海贼-王路飞"疯狂饰演多个角色答题的事件,最终以其被封杀为结果:知乎官方账号发表<知乎小管家工作笔记:我们封禁了几个伪造身份的帐号>,内容为公布了新一批 ...

  8. 4——PHP比较&&复制运算符

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  9. js数据类型大全

    声明变量的命名规范(标识符) 1.不能以数字开头,只能以字母或者¥或者_开头 2.js变量名称区分大小写 3.变量名不能含有关键字(this.if.for.while) 4.驼峰命名法 console ...

  10. 非对称加密算法RSA 学习

    非对称加密算法RSA 学习 RSA加密算法是一种非对称加密算法.RSA是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Ad ...