Skip to content

Commit 418d58a

Browse files
committed
First commit on Doyensec repo
0 parents  commit 418d58a

19 files changed

+2611
-0
lines changed

.gitignore

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Package Files #
2+
*.jar
3+
*.war
4+
*.ear
5+
6+
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
7+
hs_err_pid*
8+
9+
#ant specific
10+
dist/
11+
build/
12+
build.xml
13+
14+
#netbeans specific
15+
core
16+
nbproject/*
17+
libs/*
18+
manifest.mf
19+
.jacocoverage/
20+
coverage/
21+
22+
#java specific
23+
*.class
24+
25+
#general swap/backup files
26+
*.so
27+
*.log
28+
*.out
29+
*~
30+
*.swp
31+
*.DS_Store
32+
*.lock
33+
34+
#idea specific
35+
.classpath
36+
.project
37+
.settings
38+
.idea
39+
.metadata
40+
*.iml
41+
*.ipr
42+
**/*~
43+
/target/

README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# libajp13 - A complete AJPv1.3 Java library
2+
3+
**libajp13** is a fully featured open source library implementing the Apache JServ Protocol version 1.3 (ajp13), based on the [Apache Protocol Reference](https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html).
4+
5+
The library has been developed from Espen Wiborg's [ajp_client](https://github.com/espenhw/ajp-client), licensed under the Apache License 2.0. At this point, most of the code has been refactored and improved to support *all* AJP13 packet types.
6+
7+
![AJP13 in Wireshark](http://i.imgur.com/e9wTKDS.png "AJP13 in Wireshark")
8+
9+
As of 02/27/2017, the JaCoCoverage analysis of project "libajp13" reports:
10+
![Test Code Coverage](http://i.imgur.com/ADD_HERE_TCOVERAGE.png"Test Code Coverage")
11+
12+
### Issues
13+
This implementation is derived from Dan Milstein's reversing work, based on Tomcat 3.x AJP code. If you've discovered a bug, please open an issue in Github.
14+
15+
### How To Use it
16+
The following code examples show how to use *libajp13*.
17+
18+
For more details, please refer to the official [JavaDoc](http://doyensec.github.io/libajp13/).
19+
20+
_CPing and CPong_
21+
```java
22+
//Create a CPing message
23+
AjpMessage msg = new CPingMessage();
24+
//Send the content of the packet - msg.getBytes()
25+
[...]
26+
AjpMessage reply = AjpReader.parseMessage(gotBytes);
27+
if (reply instanceof CPongMessage) {
28+
System.out.println("[OK] Valid CPong");
29+
}
30+
```
31+
_Shutdown_
32+
```java
33+
AjpMessage msg = new ShutdownMessage();
34+
```
35+
36+
_EndResponse with session reuse set to 'true'_
37+
```java
38+
AjpMessage msg = new EndResponseMessage(true);
39+
```
40+
41+
_SendBodyChunkMessage_
42+
```java
43+
AjpMessage msg = new SendBodyChunkMessage("ABCD".getBytes());
44+
```
45+
46+
_SendHeadersMessage_
47+
```java
48+
List<Pair<String, String>> headers = new LinkedList<>();
49+
headers.add(Pair.make("Content-Type","text/html; charset=utf-8"));
50+
AjpMessage msg = new SendHeadersMessage(200,"OK",headers);
51+
```
52+
53+
_GetBodyChunkMessage_
54+
```java
55+
AjpMessage msg = new GetBodyChunkMessage(10);
56+
```
57+
58+
_BodyMessage_
59+
```java
60+
AjpMessage msg = new BodyMessage("MyStringSentAsBytes".getBytes());
61+
```
62+
63+
_ForwardRequestMessage to build a simple GET request_
64+
```java
65+
List<Pair<String, String>> headers = new LinkedList<>();
66+
headers.add(Pair.make("Content-Type","text/html; charset=utf-8"));
67+
AjpMessage msg = new ForwardRequestMessage(2, new URL("http://127.0.0.1/"), headers, null);
68+
```
69+
70+
_ForwardRequestMessage using ForwardRequestMessageGetBuilder_
71+
```java
72+
AjpMessage msg = ForwardRequestMessage.ForwardRequestMessageGetBuilder(new URL("http://192.168.1.1/log/"));
73+
```
74+
75+
_ForwardRequestMessage to build a PUT request with custom headers and attributes_
76+
```java
77+
List<Pair<String, String>> headers = new LinkedList<>();
78+
headers.add(Pair.make("Content-Type","text/html; charset=utf-8"));
79+
headers.add(Pair.make("CustomHeaderName","CustomHeaderValue"));
80+
List<Pair<String, String>> attributes = new LinkedList<>();
81+
attributes.add(Pair.make("jvm_route","3131212"));
82+
attributes.add(Pair.make("custom_attribute","custom_value"));
83+
AjpMessage msg = new ForwardRequestMessage(5, "HTTP/1.0", "/api/", "127.0.0.1", "localhost", "127.0.0.1", 8009, true, headers, attributes);
84+
```
85+
86+
### Useful links
87+
* https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
88+
* https://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/coyote/ajp/Constants.html
89+
* https://github.com/kohsuke/ajp-client
90+
* http://isu.ifmo.ru/docs/IAS904/web.904/q20202/protocol/AJPv21.html
91+
* http://en.wikipedia.org/wiki/Apache_JServ_Protocol
92+
* https://tomcat.apache.org/tomcat-7.0-doc/config/ajp.html
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* libajp13 - AbstractAjpMessage.java
3+
*
4+
* Copyright (c) 2017 Luca Carettoni - Doyensec LLC.
5+
* Copyright (c) 2010 Espen Wiborg
6+
*
7+
* Licensed under the Apache License, Version 2.0
8+
*/
9+
package com.doyensec.ajp13;
10+
11+
import java.io.ByteArrayOutputStream;
12+
import java.io.IOException;
13+
import java.io.OutputStream;
14+
import java.io.UnsupportedEncodingException;
15+
16+
/**
17+
* Partial implementation of the AJP message; all AJP messages extend this class
18+
*/
19+
public abstract class AbstractAjpMessage implements AjpMessage
20+
{
21+
22+
private final ByteArrayOutputStream bos;
23+
24+
AbstractAjpMessage(int packetType)
25+
{
26+
bos = new ByteArrayOutputStream();
27+
28+
if (packetType == Constants.PACKET_TYPE_DATA
29+
|| packetType == Constants.PACKET_TYPE_FORWARD_REQUEST
30+
|| packetType == Constants.PACKET_TYPE_SHUTDOWN
31+
|| packetType == Constants.PACKET_TYPE_PING
32+
|| packetType == Constants.PACKET_TYPE_CPING) {
33+
bos.write(Constants.AJP_TAG_REQ, 0, Constants.AJP_TAG_REQ.length);
34+
} else {
35+
bos.write(Constants.AJP_TAG_RESP, 0, Constants.AJP_TAG_RESP.length);
36+
}
37+
// Write two bytes as placeholder for the length
38+
bos.write(0);
39+
bos.write(0);
40+
// Exception for the request body packet type
41+
if (packetType != Constants.PACKET_TYPE_DATA) {
42+
bos.write(packetType);
43+
}
44+
}
45+
46+
/**
47+
* Write an AJP message to a given OutputStream
48+
*
49+
* @param OutputStream Destination output stream
50+
* @throws IOException
51+
*/
52+
@Override
53+
public void writeTo(OutputStream out) throws IOException
54+
{
55+
out.write(getBytes());
56+
out.flush();
57+
}
58+
59+
/**
60+
* Returns the byte array of the current AJPMessage instance
61+
*
62+
* @return The AJP packet as array of bytes
63+
*/
64+
@Override
65+
public final byte[] getBytes()
66+
{
67+
byte[] bytes = bos.toByteArray();
68+
int length = bytes.length - 4;
69+
if (length == -1) {
70+
bytes[2] = -1;
71+
bytes[3] = -1;
72+
} else {
73+
bytes[2] = (byte) ((length & 0xff00) >> 8);
74+
bytes[3] = (byte) (length & 0x00ff);
75+
}
76+
return bytes;
77+
}
78+
79+
/*
80+
* Four data types:
81+
* Byte - a single byte
82+
* Boolean - a single byte (1=true, 0=false)
83+
* Integer - two bytes (from 0 to 2^16)
84+
* String - variable sized string (2 bytes for size + X bytes string + \0)
85+
*/
86+
void writeByte(int b)
87+
{
88+
bos.write(b);
89+
}
90+
91+
void writeBytes(byte[] ba) throws IOException
92+
{
93+
bos.write(ba);
94+
}
95+
96+
void writeInt(int i)
97+
{
98+
bos.write((i & 0xff00) >> 8);
99+
bos.write(i & 0x00ff);
100+
}
101+
102+
void writeBoolean(boolean b)
103+
{
104+
bos.write(b ? 1 : 0);
105+
}
106+
107+
/*
108+
* @param s the string to write in the specific message
109+
* @param term whether or not append the null-byte terminator
110+
*/
111+
void writeString(String s, boolean term)
112+
{
113+
if (s == null) {
114+
bos.write(0);
115+
} else {
116+
// size (2 bytes) + string + \0
117+
writeInt(s.length());
118+
try {
119+
byte[] buf = s.getBytes("UTF-8");
120+
bos.write(buf, 0, buf.length);
121+
//From my experiments, the bodyMessage packet doesn't contain the string terminator (mistake?)
122+
if (term) {
123+
bos.write('\0');
124+
}
125+
} catch (UnsupportedEncodingException ex) {
126+
System.out.println("[!] AbstractAjpMessage UnsupportedEncodingException: " + ex.getLocalizedMessage());
127+
}
128+
}
129+
}
130+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* libajp13 - AjpMessage.java
3+
*
4+
* Copyright (c) 2017 Luca Carettoni - Doyensec LLC.
5+
* Copyright (c) 2010 Espen Wiborg
6+
*
7+
* Licensed under the Apache License, Version 2.0
8+
*/
9+
package com.doyensec.ajp13;
10+
11+
import java.io.IOException;
12+
import java.io.OutputStream;
13+
14+
/**
15+
* Generic interface for all AJP messages (requests and responses)
16+
*/
17+
public interface AjpMessage
18+
{
19+
20+
byte[] getBytes(); //returns the raw bytes
21+
22+
void writeTo(OutputStream out) throws IOException; //writes to a given outputstream
23+
24+
String getName(); //returns a meaningful name for the packet type
25+
26+
String getDescription(); //returns a description for the packet type, with actual field values
27+
}

0 commit comments

Comments
 (0)