浅析dir-815服务架构(一)

D-Link DIR-815栈溢出漏洞,现已是经典的入门实战案例。笔者在复现的过程中,总觉得只是一知半解,阅读源代码时,不知为何会有这些操作。因此,笔者希望能够真正明白该路由器的web服务原理,在以后分析其他路由器时,也能够有所经验。

请求网页时发生了什么

当我们输入一个URL后,浏览器就会渲染出一个页面,那么这个页面,以及其中的内容是怎么来的,我们不难从网络上查到以下历程。

  1. 浏览器通过DNS把域名解析成对应的IP地址。
  2. 浏览器和服务器建立连接(TCP/TP三次握手)。
  3. 浏览器向服务器发送HTTP请求。
  4. 服务器接受到请求并返回HTTP响应。
  5. 浏览器解析渲染页面。

这里我们不去关心过程1、2,我们只在http层面做分析。

第一个http请求报文

当我们输入一个URL,浏览器就会向通过DNS解析得到的ip地址发送第一个http请求。这里笔者以打开百度为例,使用brupsuite拦截了第一个请求报文。

GET / HTTP/1.1
Host: www.baidu.com
Cookie: ***
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Linux"
Upgrade-Insecure-Requests: 1
User-Agent: ***
Accept: text/html,application/xhtml+xml,application/xml; \
q=0.9,image/avif,image/webp,image/apng,*/*; \
q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

可以看出这个报文使用GET请求,请求资源为根 /,主机为www.baidu.com,没有其他什么特别的内容。

资源文件的获取

接下来笔者的一个疑问是,我们浏览器在打开页面时,是获取了一堆(包括.css,.js在内的)资源文件,然后渲染的页面,那么这些文件是如何获取、何时获取的呢?
image.png
当服务器接到我们GET请求的http报文后,会向我们发回一个http应答。这个应答包含响应头和主体内容,主体内容为html格式。
image.png
接下来浏览器就会尝试解析该html,构建DOM树,在浏览器解析的过程中,如果遇到link、script、img等标签,就会向服务器发送HTTP请求获取相应的资源文件。
image.png
image.png
当资源就位后,浏览器就会根据渲染树,绘制页面内容到屏幕上。

php

浏览器完成页面的渲染离不开html、css、js这前端三剑客的相互配合,即便在vue、react等前端框架盛行的当下,浏览器也需要这些框架导出的html、css、js文件。DIR-815路由器,使用php与cgi完成业务逻辑。这里简单介绍一下php的原理。

php语法

PHP 脚本以 结束:

<?php
function functionName()
{
    echo "Hello World!";
}
?>

<!DOCTYPE html>
<html>
<body>

<h1>My first PHP page</h1>

<?php
echo "Hello World!";
?>

</body>
</html>

可以简单的理解php文件就是在html文件的基础上加了一些动态的内容,这些php的内容可以加在html中间,也可以在其前后。当客户端请求php文件后,php文件会根据请求的参数输出不同的内容,客户端得到回应是不含php语句的。

<!DOCTYPE html>
<html>
<body>

<h1>My first PHP page</h1>

Hello World!

</body>
</html>

ajax

image.png
分析固件导出的文件系统,我们可以发现对hedwig.cgi请求,在文件/htdocs/web/js/comm.js中,是通过ajax实现的。

function COMM_CallHedwig(xml, resultCallback)
{
  var ajaxObj = GetAjaxObj("setData");
  ajaxObj.createRequest();
  ajaxObj.onCallback =
    function (xml)
    {
      ajaxObj.release();
      resultCallback(xml);
    }
  ajaxObj.setHeader("Content-Type", "text/xml");
  ajaxObj.sendRequest("hedwig.cgi", xml.XDoc);
}

ajax并不是一种编程语言,它是web2.0引入的一种写在js中,不需要刷新整个页面就可以更新页面。它允许js向服务器发送http报文,根据回复更新网页的部分信息。

ajax使用

我们知道js可以给一个按钮绑定一个回调函数,下面就是一个使用ajax作为回调函数的例子。

function showHint(str)
{
    var xmlhttp;
    xmlhttp=new XMLHttpRequest();
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState==4 && xmlhttp.status==200)
        {
            document.getElementById("txtHint").innerHTML=xmlhttp.responseText;
        }
    }
    xmlhttp.open("GET","/try/ajax/gethint.php?q="+str,true);
    xmlhttp.send();
}

ajax使用XMLHttpRequest()来实现,首先编写了一个onreadystatechange回调函数作为获得服务器相应后的处理方式,这里即将ID为txtHint元素的内容,修改为了xmlhttpresponseTextxmlhttp.open设置了请求内容,xmlhttp.send()则发起了请求。

ajax在dir-815中的实现

function COMM_CallHedwig(xml, resultCallback)
{
	var ajaxObj = GetAjaxObj("setData");
	ajaxObj.createRequest();
	ajaxObj.onCallback =
	function (xml)
	{
		ajaxObj.release();
		resultCallback(xml);
	}
	ajaxObj.setHeader("Content-Type", "text/xml");
	ajaxObj.sendRequest("hedwig.cgi", xml.XDoc);
}

COMM_CallHedwig即可作为一个回调函数,只不过它将获取xml响应后的操作,抽象了出来,可以由其他模块调用时填入。这里的GetAjaxObj()函数,会返回对应传入参数的HTTPClient()对象,如果之前没有,没有就会新创建一个。ajaxObj.createRequest()创建了一个XMLHttpRequest()对象,接下来将定义onCallback,在release后,调用resultCallback(xml),使用COMM_CallHedwig(xml, resultCallback)传入的对象。ajaxObj.setHeader设置header,最后ajaxObj.sendRequest("hedwig.cgi", xml.XDoc)发送请求。
我们接下来看一下HTTPClient()对象,这里我省去部分内容,只保留上述结果使用了的部分。

function HTTPClient(){}
HTTPClient.prototype =
{
	debug: false,
	__httpRequest : null,
	requestMethod : "POST",
	requestAsyn : true,
	returnXml : true,
	__header : null,
	onSend : null,
	onCallback : null,
	onError : function (msg){......},
	__callback : function(){
    ......
    this.onCallback,
    ......
  },
	createRequest : function()
	{
		try
		{
			this.__httpRequest = new XMLHttpRequest();
		}
		catch (e){......}
	},
	sendRequest : function(requestUrl, payload)
	{
		if (!this.__httpRequest) this.createRequest();
		var self = this;
		this.__httpRequest.onreadystatechange = function() {self.__callback();}
		if (!requestUrl)
		{
			this.onError("Error : Invalid request URL.");
			return;
		}
		this.__httpRequest.open(this.requestMethod, requestUrl, this.requestAsyn);
		if (this.__header)
		{
			for (var i = 0; i < this.__header.length; i+=1)
			{
				if (this.__header[i].value != "")
					this.__httpRequest.setRequestHeader(this.__header[i].name, this.__header[i].value);
			}
		}
		if (this.requestMethod == "GET" || this.requestMethod == "get")
			this.__httpRequest.send(null);
		else
		{
			if (!payload)
			{
				this.onError("Error : Invalid payload for POST.");
				return;
			}
			this.__httpRequest.send(payload);
		}
	},
	getResponseHeader : function(header){......},
	getAllResponseHeaders : function(){......},
	setHeader : function(header, value)
	{
		if (header && value)
		{
			if (!this.__header) this.__header = new Array();
			var tmpHeader = new Object();
			tmpHeader.name = header;
			tmpHeader.value = value;
			this.__header[ this.__header.length ] = tmpHeader;
		}
	},
	clearHeader : function (header){......}
	clearAllHeaders : function(){......}
	release : function(){......}
};

我们看sendRequest("hedwig.cgi", xml.XDoc)传入参数的处理方式。其中"hedwig.cgi"this.__httpRequest.open(this.requestMethod, requestUrl, this.requestAsyn)也就是通过XMLHttpRequest()的open使用,HTTPClien默认使用POST方法,因此最终调用this.__httpRequest.send(payload)使用xml.XDoc
XMLHttpRequest()是浏览器内置的可调用对象,最终会向服务器发送http报文,我们不用去这个层面分析。

posted @ 2023-03-01 21:40  benoqtr  阅读(58)  评论(0编辑  收藏  举报