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. 吴裕雄--天生自然 R语言开发学习:基本统计分析(续三)

    #---------------------------------------------------------------------# # R in Action (2nd ed): Chap ...

  2. 关于log4j中log4j.properties和log4j.xml的加载顺序

    如果采用log4j输出日志,要对log4j加载配置文件的过程有所了解. log4j启动时,默认会寻找source folder下的log4j.xml配置文件,若没有,会寻找log4j.properti ...

  3. Selenium&Pytesseract模拟登录+验证码识别

    验证码是爬虫需要解决的问题,因为很多网站的数据是需要登录成功后才可以获取的. 验证码识别,即图片识别,很多人都有误区,觉得这是爬虫方面的知识,其实是不对的. 验证码识别涉及到的知识:人工智能,模式识别 ...

  4. Archlinux 自动挂载移动硬盘,开机自动启动smb服务

    Archlinux + Raspberry 打造NAS: samba篇 树莓派自动挂载硬盘,并开启smb服务. 开机自动挂在移动硬盘ntfs 安装ntfs-3g sudo pacman -S ntfs ...

  5. 自动化测试ROI实践

    自动化测试是一项"一旦开始,就需要持续投入"的工作,所以它一直是测试领域的一块鸡肋.不做吧,好像手工测试重复得让人有些厌倦,而且手工测试时间也缩短不了.做吧,害怕投入的比回报要多. ...

  6. 再谈拍照,OPPO这次拿什么和iPhone7拼?

    ​一年一度的iPhone新机如期而至,双摄像头成为iPhone 7 Plus标配,尽管在这之前,双摄像头已有少数厂商在手机上装备,但苹果一出,市场必定全面跟进.无论各大厂商是否采用双摄像头,在手机拍照 ...

  7. 初识Spring JdbcTemplate

    JdbcTemplate 概述 JdbcTemplate是Spring提供的一个模板类,它是对jdbc的封装.用于支持持久层的操作.具有简单,方便等特点. pom.xml <!--依赖版本--& ...

  8. 9——PHP循环结构foreach用法

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

  9. 【深入理解Java虚拟机 】类加载器的命名空间以及类的卸载

    类加载器的命名空间 每个类加载器又有一个命名空间,由其以及其父加载器组成 类加载器的命名空间的作用和影响 每个类加载器又有一个命名空间,由其以及其父加载器组成 在每个类加载器自己的命名空间中不能出现相 ...

  10. java ThreadPoolExecutor初探

    导读:线程池是开发中使用频率比较高的组件之一,但是又有多少人真正了解其内部机制呢. 关键词:线程池 前言 线程池是大家开发过程中使用频率比较高的组件之一,但是其内部原理又有多少人真正清楚呢.最近抽时间 ...