XMPP service discovery (XEP-0030) is designed to let clients learn what a server can do, and it answers on a stream that has not authenticated. Two queries carry it: disco#info returns a server's or component's supported features (identities and feature namespaces), and disco#items returns the child items, which on a server are its components, the hosted multi-user-chat service, file-transfer proxies, and often an admin component. From that you learn the server software, the hosted vhosts, and whether capabilities like in-band registration are enabled, all before you have an account. User existence is then probed with targeted stanzas.
Preconditions#
An opened stream (the server answered your <stream:stream> with <stream:features>). Discovery works before TLS and before SASL on most deployments; if the server returns <not-authorized/> to disco, it requires an authenticated session, in which case self-provision one first via authentication. Know the server's domain (the to= of the stream), since components are named as subdomains of it.
Worked service discovery#
Send an <iq> of type get carrying the disco#items query, then drill into each returned component with disco#info:
<!-- list the server's components and services -->
<iq type='get' id='d1' to='target.lan'>
<query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>
<!-- server response: each item is a component you can target next -->
<iq type='result' id='d1' from='target.lan'>
<query xmlns='http://jabber.org/protocol/disco#items'>
<item jid='conference.target.lan' name='Chatrooms'/>
<item jid='proxy.target.lan' name='SOCKS5 Bytestreams'/>
<item jid='pubsub.target.lan' name='Publish-Subscribe'/>
</query>
</iq>
<!-- ask a component what it supports and what it is -->
<iq type='get' id='d2' to='target.lan'>
<query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>
<iq type='result' id='d2' from='target.lan'>
<query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='server' type='im' name='Prosody'/>
<feature var='jabber:iq:register'/>
<feature var='http://jabber.org/protocol/muc'/>
</query>
</iq>
Interpretation: each <item> is a live component, naming the multi-user-chat service (conference.), file proxy, and pubsub node to enumerate further. The <identity ... name='Prosody'/> fingerprints the daemon for server exploitation, and the advertised <feature var='jabber:iq:register'/> confirms in-band registration is on, so you can self-provision an account without any credential (see authentication).
Enumerate the chat rooms on the MUC component the same way:
<iq type='get' id='r1' to='conference.target.lan'>
<query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>
<!-- each returned <item jid='ops@conference.target.lan' name='ops'/> is a room to join -->
Probing user existence and vhosts#
<!-- does this user exist? a vCard request distinguishes a real JID from an empty one -->
<iq type='get' id='v1' to='jdoe@target.lan'><vCard xmlns='vcard-temp'/></iq>
<!-- type='result' with content => user exists; item-not-found => no such account -->
<!-- is in-band registration open, and what fields does it want? -->
<iq type='get' id='reg1' to='target.lan'><query xmlns='jabber:iq:register'/></iq>
<!-- a returned <username/><password/> form => self-provisioning available -->
A type='result' vCard with populated fields (or presence/last-activity data) confirms the JID is real, while <item-not-found/> or an empty result means it is not; iterate a name list to build the user inventory. Additional vhosts appear as extra server domains in disco or in the stream features; repeat discovery against each.
Follow-on#
- The component list names exactly what to attack next: the MUC service for rooms, the admin component for privileged operations, and any HTTP-upload or proxy component for file abuse.
- A confirmed
jabber:iq:registerform means you can mint your own account immediately; go to authentication. - Enumerated JIDs feed SASL brute force and the roster of real users to impersonate once you hold an account.